<!doctype html>
<html class="no-js" lang="en" data-content_root="../../">
  <head><meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html"><link rel="search" title="Search" href="../../search.html">

    <!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25 -->
        <title>osxphotos.phototemplate - osxphotos 0.74.2 documentation</title>
      <link rel="stylesheet" type="text/css" href="../../_static/pygments.css?v=d111a655" />
    <link rel="stylesheet" type="text/css" href="../../_static/styles/furo.css?v=580074bf" />
    <link rel="stylesheet" type="text/css" href="../../_static/copybutton.css?v=76b2166b" />
    <link rel="stylesheet" type="text/css" href="../../_static/styles/furo-extensions.css?v=8dab3a3b" />
    
    


<style>
  body {
    --color-code-background: #f2f2f2;
  --color-code-foreground: #1e1e1e;
  
  }
  @media not print {
    body[data-theme="dark"] {
      --color-code-background: #202020;
  --color-code-foreground: #d0d0d0;
  
    }
    @media (prefers-color-scheme: dark) {
      body:not([data-theme="light"]) {
        --color-code-background: #202020;
  --color-code-foreground: #d0d0d0;
  
      }
    }
  }
</style></head>
  <body>
    
    <script>
      document.body.dataset.theme = localStorage.getItem("theme") || "auto";
    </script>
    

<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
  <symbol id="svg-toc" viewBox="0 0 24 24">
    <title>Contents</title>
    <svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 1024 1024">
      <path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM115.4 518.9L271.7 642c5.8 4.6 14.4.5 14.4-6.9V388.9c0-7.4-8.5-11.5-14.4-6.9L115.4 505.1a8.74 8.74 0 0 0 0 13.8z"/>
    </svg>
  </symbol>
  <symbol id="svg-menu" viewBox="0 0 24 24">
    <title>Menu</title>
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
      stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-menu">
      <line x1="3" y1="12" x2="21" y2="12"></line>
      <line x1="3" y1="6" x2="21" y2="6"></line>
      <line x1="3" y1="18" x2="21" y2="18"></line>
    </svg>
  </symbol>
  <symbol id="svg-arrow-right" viewBox="0 0 24 24">
    <title>Expand</title>
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
      stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-chevron-right">
      <polyline points="9 18 15 12 9 6"></polyline>
    </svg>
  </symbol>
  <symbol id="svg-sun" viewBox="0 0 24 24">
    <title>Light mode</title>
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
      stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="feather-sun">
      <circle cx="12" cy="12" r="5"></circle>
      <line x1="12" y1="1" x2="12" y2="3"></line>
      <line x1="12" y1="21" x2="12" y2="23"></line>
      <line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
      <line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
      <line x1="1" y1="12" x2="3" y2="12"></line>
      <line x1="21" y1="12" x2="23" y2="12"></line>
      <line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
      <line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
    </svg>
  </symbol>
  <symbol id="svg-moon" viewBox="0 0 24 24">
    <title>Dark mode</title>
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
      stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-moon">
      <path stroke="none" d="M0 0h24v24H0z" fill="none" />
      <path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z" />
    </svg>
  </symbol>
  <symbol id="svg-sun-with-moon" viewBox="0 0 24 24">
    <title>Auto light/dark, in light mode</title>
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
      stroke-width="1" stroke-linecap="round" stroke-linejoin="round"
      class="icon-custom-derived-from-feather-sun-and-tabler-moon">
      <path style="opacity: 50%" d="M 5.411 14.504 C 5.471 14.504 5.532 14.504 5.591 14.504 C 3.639 16.319 4.383 19.569 6.931 20.352 C 7.693 20.586 8.512 20.551 9.25 20.252 C 8.023 23.207 4.056 23.725 2.11 21.184 C 0.166 18.642 1.702 14.949 4.874 14.536 C 5.051 14.512 5.231 14.5 5.411 14.5 L 5.411 14.504 Z"/>
      <line x1="14.5" y1="3.25" x2="14.5" y2="1.25"/>
      <line x1="14.5" y1="15.85" x2="14.5" y2="17.85"/>
      <line x1="10.044" y1="5.094" x2="8.63" y2="3.68"/>
      <line x1="19" y1="14.05" x2="20.414" y2="15.464"/>
      <line x1="8.2" y1="9.55" x2="6.2" y2="9.55"/>
      <line x1="20.8" y1="9.55" x2="22.8" y2="9.55"/>
      <line x1="10.044" y1="14.006" x2="8.63" y2="15.42"/>
      <line x1="19" y1="5.05" x2="20.414" y2="3.636"/>
      <circle cx="14.5" cy="9.55" r="3.6"/>
    </svg>
  </symbol>
  <symbol id="svg-moon-with-sun" viewBox="0 0 24 24">
    <title>Auto light/dark, in dark mode</title>
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
      stroke-width="1" stroke-linecap="round" stroke-linejoin="round"
      class="icon-custom-derived-from-feather-sun-and-tabler-moon">
      <path d="M 8.282 7.007 C 8.385 7.007 8.494 7.007 8.595 7.007 C 5.18 10.184 6.481 15.869 10.942 17.24 C 12.275 17.648 13.706 17.589 15 17.066 C 12.851 22.236 5.91 23.143 2.505 18.696 C -0.897 14.249 1.791 7.786 7.342 7.063 C 7.652 7.021 7.965 7 8.282 7 L 8.282 7.007 Z"/>
      <line style="opacity: 50%" x1="18" y1="3.705" x2="18" y2="2.5"/>
      <line style="opacity: 50%" x1="18" y1="11.295" x2="18" y2="12.5"/>
      <line style="opacity: 50%" x1="15.316" y1="4.816" x2="14.464" y2="3.964"/>
      <line style="opacity: 50%" x1="20.711" y1="10.212" x2="21.563" y2="11.063"/>
      <line style="opacity: 50%" x1="14.205" y1="7.5" x2="13.001" y2="7.5"/>
      <line style="opacity: 50%" x1="21.795" y1="7.5" x2="23" y2="7.5"/>
      <line style="opacity: 50%" x1="15.316" y1="10.184" x2="14.464" y2="11.036"/>
      <line style="opacity: 50%" x1="20.711" y1="4.789" x2="21.563" y2="3.937"/>
      <circle style="opacity: 50%" cx="18" cy="7.5" r="2.169"/>
    </svg>
  </symbol>
  <symbol id="svg-pencil" viewBox="0 0 24 24">
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
      stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-pencil-code">
      <path d="M4 20h4l10.5 -10.5a2.828 2.828 0 1 0 -4 -4l-10.5 10.5v4" />
      <path d="M13.5 6.5l4 4" />
      <path d="M20 21l2 -2l-2 -2" />
      <path d="M17 17l-2 2l2 2" />
    </svg>
  </symbol>
  <symbol id="svg-eye" viewBox="0 0 24 24">
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
      stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-eye-code">
      <path stroke="none" d="M0 0h24v24H0z" fill="none" />
      <path d="M10 12a2 2 0 1 0 4 0a2 2 0 0 0 -4 0" />
      <path
        d="M11.11 17.958c-3.209 -.307 -5.91 -2.293 -8.11 -5.958c2.4 -4 5.4 -6 9 -6c3.6 0 6.6 2 9 6c-.21 .352 -.427 .688 -.647 1.008" />
      <path d="M20 21l2 -2l-2 -2" />
      <path d="M17 17l-2 2l2 2" />
    </svg>
  </symbol>
</svg>

<input type="checkbox" class="sidebar-toggle" name="__navigation" id="__navigation" aria-label="Toggle site navigation sidebar">
<input type="checkbox" class="sidebar-toggle" name="__toc" id="__toc" aria-label="Toggle table of contents sidebar">
<label class="overlay sidebar-overlay" for="__navigation"></label>
<label class="overlay toc-overlay" for="__toc"></label>

<a class="skip-to-content muted-link" href="#furo-main-content">Skip to content</a>



<div class="page">
  <header class="mobile-header">
    <div class="header-left">
      <label class="nav-overlay-icon" for="__navigation">
        <span class="icon"><svg><use href="#svg-menu"></use></svg></span>
      </label>
    </div>
    <div class="header-center">
      <a href="../../index.html"><div class="brand">osxphotos 0.74.2 documentation</div></a>
    </div>
    <div class="header-right">
      <div class="theme-toggle-container theme-toggle-header">
        <button class="theme-toggle" aria-label="Toggle Light / Dark / Auto color theme">
          <svg class="theme-icon-when-auto-light"><use href="#svg-sun-with-moon"></use></svg>
          <svg class="theme-icon-when-auto-dark"><use href="#svg-moon-with-sun"></use></svg>
          <svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
          <svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
        </button>
      </div>
      <label class="toc-overlay-icon toc-header-icon no-toc" for="__toc">
        <span class="icon"><svg><use href="#svg-toc"></use></svg></span>
      </label>
    </div>
  </header>
  <aside class="sidebar-drawer">
    <div class="sidebar-container">
      
      <div class="sidebar-sticky"><a class="sidebar-brand" href="../../index.html">
  
  <span class="sidebar-brand-text">osxphotos 0.74.2 documentation</span>
  
</a><form class="sidebar-search-container" method="get" action="../../search.html" role="search">
  <input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
  <input type="hidden" name="check_keywords" value="yes">
  <input type="hidden" name="area" value="default">
</form>
<div id="searchbox"></div><div class="sidebar-scroll"><div class="sidebar-tree">
  <ul>
<li class="toctree-l1"><a class="reference internal" href="../../overview.html">OSXPhotos</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../tutorial.html">OSXPhotos Tutorial</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../cli.html">OSXPhotos Command Line Interface (CLI)</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../template_help.html">OSXPhotos Template System</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../package_overview.html">OSXPhotos Python Package Overview</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../API_README.html">OSXPhotos Python API</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos Python Reference</a></li>
</ul>

</div>
</div>

      </div>
      
    </div>
  </aside>
  <div class="main">
    <div class="content">
      <div class="article-container">
        <a href="#" class="back-to-top muted-link">
          <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
            <path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8v12z"></path>
          </svg>
          <span>Back to top</span>
        </a>
        <div class="content-icon-container">
          <div class="theme-toggle-container theme-toggle-content">
            <button class="theme-toggle" aria-label="Toggle Light / Dark / Auto color theme">
              <svg class="theme-icon-when-auto-light"><use href="#svg-sun-with-moon"></use></svg>
              <svg class="theme-icon-when-auto-dark"><use href="#svg-moon-with-sun"></use></svg>
              <svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
              <svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
            </button>
          </div>
          <label class="toc-overlay-icon toc-content-icon no-toc" for="__toc">
            <span class="icon"><svg><use href="#svg-toc"></use></svg></span>
          </label>
        </div>
        <article role="main" id="furo-main-content">
          <h1>Source code for osxphotos.phototemplate</h1><div class="highlight"><pre>
<span></span><span class="sd">&quot;&quot;&quot;Custom template system for osxphotos, implements metadata template language (MTL)&quot;&quot;&quot;</span>

<span class="kn">import</span><span class="w"> </span><span class="nn">datetime</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">locale</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">os</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">pathlib</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">re</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">shlex</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">sys</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">contextlib</span><span class="w"> </span><span class="kn">import</span> <span class="n">suppress</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">dataclasses</span><span class="w"> </span><span class="kn">import</span> <span class="n">dataclass</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">typing</span><span class="w"> </span><span class="kn">import</span> <span class="n">TYPE_CHECKING</span><span class="p">,</span> <span class="n">List</span><span class="p">,</span> <span class="n">Optional</span><span class="p">,</span> <span class="n">Tuple</span>

<span class="kn">from</span><span class="w"> </span><span class="nn">textx</span><span class="w"> </span><span class="kn">import</span> <span class="n">TextXSyntaxError</span><span class="p">,</span> <span class="n">metamodel_from_file</span>

<span class="kn">import</span><span class="w"> </span><span class="nn">osxphotos.template_counter</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="nn">counter</span>

<span class="kn">from</span><span class="w"> </span><span class="nn">._constants</span><span class="w"> </span><span class="kn">import</span> <span class="n">_UNKNOWN_PERSON</span><span class="p">,</span> <span class="n">TEXT_DETECTION_CONFIDENCE_THRESHOLD</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">._version</span><span class="w"> </span><span class="kn">import</span> <span class="n">__version__</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">.datetime_formatter</span><span class="w"> </span><span class="kn">import</span> <span class="n">DateTimeFormatter</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">.exiftool</span><span class="w"> </span><span class="kn">import</span> <span class="n">ExifToolCaching</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">.path_utils</span><span class="w"> </span><span class="kn">import</span> <span class="n">sanitize_dirname</span><span class="p">,</span> <span class="n">sanitize_filename</span><span class="p">,</span> <span class="n">sanitize_pathpart</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">.utils</span><span class="w"> </span><span class="kn">import</span> <span class="p">(</span>
    <span class="n">download_url_to_temp_dir</span><span class="p">,</span>
    <span class="n">expand_and_validate_filepath</span><span class="p">,</span>
    <span class="n">is_http_url</span><span class="p">,</span>
    <span class="n">load_function</span><span class="p">,</span>
    <span class="n">uuid_to_shortuuid</span><span class="p">,</span>
<span class="p">)</span>

<span class="k">if</span> <span class="n">TYPE_CHECKING</span><span class="p">:</span>
    <span class="kn">from</span><span class="w"> </span><span class="nn">.photoinfo</span><span class="w"> </span><span class="kn">import</span> <span class="n">PhotoInfo</span>

<span class="n">__all__</span> <span class="o">=</span> <span class="p">[</span>
    <span class="s2">&quot;RenderOptions&quot;</span><span class="p">,</span>
    <span class="s2">&quot;PhotoTemplateParser&quot;</span><span class="p">,</span>
    <span class="s2">&quot;PhotoTemplate&quot;</span><span class="p">,</span>
    <span class="s2">&quot;parse_default_kv&quot;</span><span class="p">,</span>
    <span class="s2">&quot;get_template_help&quot;</span><span class="p">,</span>
    <span class="s2">&quot;format_str_value&quot;</span><span class="p">,</span>
<span class="p">]</span>

<span class="c1"># TODO: a lot of values are passed from function to function like path_sep--make these all class properties</span>

<span class="c1"># ensure locale set to user&#39;s locale</span>
<span class="n">locale</span><span class="o">.</span><span class="n">setlocale</span><span class="p">(</span><span class="n">locale</span><span class="o">.</span><span class="n">LC_ALL</span><span class="p">,</span> <span class="s2">&quot;&quot;</span><span class="p">)</span>

<span class="n">MTL_GRAMMAR_MODEL</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="vm">__file__</span><span class="p">)</span><span class="o">.</span><span class="n">parent</span> <span class="o">/</span> <span class="s2">&quot;phototemplate.tx&quot;</span><span class="p">)</span>

<span class="sd">&quot;&quot;&quot;TextX metamodel for osxphotos template language &quot;&quot;&quot;</span>

<span class="n">PHOTO_VIDEO_TYPE_DEFAULTS</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;photo&quot;</span><span class="p">:</span> <span class="s2">&quot;photo&quot;</span><span class="p">,</span> <span class="s2">&quot;video&quot;</span><span class="p">:</span> <span class="s2">&quot;video&quot;</span><span class="p">}</span>

<span class="n">MEDIA_TYPE_DEFAULTS</span> <span class="o">=</span> <span class="p">{</span>
    <span class="s2">&quot;selfie&quot;</span><span class="p">:</span> <span class="s2">&quot;selfie&quot;</span><span class="p">,</span>
    <span class="s2">&quot;time_lapse&quot;</span><span class="p">:</span> <span class="s2">&quot;time_lapse&quot;</span><span class="p">,</span>
    <span class="s2">&quot;panorama&quot;</span><span class="p">:</span> <span class="s2">&quot;panorama&quot;</span><span class="p">,</span>
    <span class="s2">&quot;slow_mo&quot;</span><span class="p">:</span> <span class="s2">&quot;slow_mo&quot;</span><span class="p">,</span>
    <span class="s2">&quot;screenshot&quot;</span><span class="p">:</span> <span class="s2">&quot;screenshot&quot;</span><span class="p">,</span>
    <span class="s2">&quot;screen_recording&quot;</span><span class="p">:</span> <span class="s2">&quot;screen_recording&quot;</span><span class="p">,</span>
    <span class="s2">&quot;portrait&quot;</span><span class="p">:</span> <span class="s2">&quot;portrait&quot;</span><span class="p">,</span>
    <span class="s2">&quot;live_photo&quot;</span><span class="p">:</span> <span class="s2">&quot;live_photo&quot;</span><span class="p">,</span>
    <span class="s2">&quot;burst&quot;</span><span class="p">:</span> <span class="s2">&quot;burst&quot;</span><span class="p">,</span>
    <span class="s2">&quot;photo&quot;</span><span class="p">:</span> <span class="s2">&quot;photo&quot;</span><span class="p">,</span>
    <span class="s2">&quot;video&quot;</span><span class="p">:</span> <span class="s2">&quot;video&quot;</span><span class="p">,</span>
<span class="p">}</span>

<span class="c1"># Permitted substitutions (each of these returns a single value or None)</span>
<span class="n">TEMPLATE_SUBSTITUTIONS</span> <span class="o">=</span> <span class="p">{</span>
    <span class="s2">&quot;</span><span class="si">{name}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;Current filename of the photo&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{original_name}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;Photo&#39;s original filename when imported to Photos&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{title}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;Title of the photo&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{descr}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;Description of the photo&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{media_type}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="p">(</span>
        <span class="sa">f</span><span class="s2">&quot;Special media type resolved in this precedence: </span><span class="si">{</span><span class="s1">&#39;, &#39;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">t</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="ow">in</span><span class="w"> </span><span class="n">MEDIA_TYPE_DEFAULTS</span><span class="p">)</span><span class="si">}</span><span class="s2">. &quot;</span>
        <span class="s2">&quot;Defaults to &#39;photo&#39; or &#39;video&#39; if no special type. &quot;</span>
        <span class="s2">&quot;Customize one or more media types using format: &#39;{media_type,video=vidéo;time_lapse=vidéo_accélérée}&#39;&quot;</span>
    <span class="p">),</span>
    <span class="s2">&quot;</span><span class="si">{photo_or_video}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;&#39;photo&#39; or &#39;video&#39; depending on what type the image is. To customize, use default value as in &#39;{photo_or_video,photo=fotos;video=videos}&#39;&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{hdr}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;Photo is HDR?; True/False value, use in format &#39;{hdr?VALUE_IF_TRUE,VALUE_IF_FALSE}&#39;&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{edited}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;True if photo has been edited (has adjustments), otherwise False; use in format &#39;{edited?VALUE_IF_TRUE,VALUE_IF_FALSE}&#39;&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{edited_version}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;True if template is being rendered for the edited version of a photo, otherwise False. &quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{favorite}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;Photo has been marked as favorite?; True/False value, use in format &#39;{favorite?VALUE_IF_TRUE,VALUE_IF_FALSE}&#39;&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{created}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;Photo&#39;s creation date in ISO format, e.g. &#39;2020-03-22&#39;&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{created.date}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;Photo&#39;s creation date in ISO format, e.g. &#39;2020-03-22&#39;&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{created.year}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;4-digit year of photo creation time&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{created.yy}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;2-digit year of photo creation time&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{created.mm}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;2-digit month of the photo creation time (zero padded)&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{created.month}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;Month name in user&#39;s locale of the photo creation time&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{created.mon}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;Month abbreviation in the user&#39;s locale of the photo creation time&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{created.dd}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;2-digit day of the month (zero padded) of photo creation time&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{created.dow}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;Day of week in user&#39;s locale of the photo creation time&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{created.doy}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;3-digit day of year (e.g Julian day) of photo creation time, starting from 1 (zero padded)&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{created.hour}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;2-digit hour of the photo creation time&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{created.min}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;2-digit minute of the photo creation time&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{created.sec}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;2-digit second of the photo creation time&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{created.strftime}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;Apply strftime template to file creation date/time. Should be used in form &quot;</span>
    <span class="o">+</span> <span class="s2">&quot;{created.strftime,TEMPLATE} where TEMPLATE is a valid strftime template, e.g. &quot;</span>
    <span class="o">+</span> <span class="s2">&quot;{created.strftime,%Y-%U} would result in year-week number of year: &#39;2020-23&#39;. &quot;</span>
    <span class="o">+</span> <span class="s2">&quot;If used with no template will return null value. &quot;</span>
    <span class="o">+</span> <span class="s2">&quot;See https://strftime.org/ for help on strftime templates.&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{modified}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;Photo&#39;s modification date in ISO format, e.g. &#39;2020-03-22&#39;; uses creation date if photo is not modified&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{modified.date}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;Photo&#39;s modification date in ISO format, e.g. &#39;2020-03-22&#39;; uses creation date if photo is not modified&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{modified.year}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;4-digit year of photo modification time; uses creation date if photo is not modified&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{modified.yy}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;2-digit year of photo modification time; uses creation date if photo is not modified&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{modified.mm}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;2-digit month of the photo modification time (zero padded); uses creation date if photo is not modified&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{modified.month}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;Month name in user&#39;s locale of the photo modification time; uses creation date if photo is not modified&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{modified.mon}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;Month abbreviation in the user&#39;s locale of the photo modification time; uses creation date if photo is not modified&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{modified.dd}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;2-digit day of the month (zero padded) of the photo modification time; uses creation date if photo is not modified&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{modified.dow}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;Day of week in user&#39;s locale of the photo modification time; uses creation date if photo is not modified&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{modified.doy}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;3-digit day of year (e.g Julian day) of photo modification time, starting from 1 (zero padded); uses creation date if photo is not modified&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{modified.hour}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;2-digit hour of the photo modification time; uses creation date if photo is not modified&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{modified.min}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;2-digit minute of the photo modification time; uses creation date if photo is not modified&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{modified.sec}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;2-digit second of the photo modification time; uses creation date if photo is not modified&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{modified.strftime}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;Apply strftime template to file modification date/time. Should be used in form &quot;</span>
    <span class="o">+</span> <span class="s2">&quot;{modified.strftime,TEMPLATE} where TEMPLATE is a valid strftime template, e.g. &quot;</span>
    <span class="o">+</span> <span class="s2">&quot;{modified.strftime,%Y-%U} would result in year-week number of year: &#39;2020-23&#39;. &quot;</span>
    <span class="o">+</span> <span class="s2">&quot;If used with no template will return null value. Uses creation date if photo is not modified. &quot;</span>
    <span class="o">+</span> <span class="s2">&quot;See https://strftime.org/ for help on strftime templates.&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{today}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;Current date in iso format, e.g. &#39;2020-03-22&#39;&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{today.date}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;Current date in iso format, e.g. &#39;2020-03-22&#39;&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{today.year}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;4-digit year of current date&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{today.yy}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;2-digit year of current date&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{today.mm}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;2-digit month of the current date (zero padded)&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{today.month}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;Month name in user&#39;s locale of the current date&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{today.mon}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;Month abbreviation in the user&#39;s locale of the current date&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{today.dd}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;2-digit day of the month (zero padded) of current date&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{today.dow}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;Day of week in user&#39;s locale of the current date&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{today.doy}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;3-digit day of year (e.g Julian day) of current date, starting from 1 (zero padded)&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{today.hour}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;2-digit hour of the current date&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{today.min}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;2-digit minute of the current date&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{today.sec}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;2-digit second of the current date&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{today.strftime}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;Apply strftime template to current date/time. Should be used in form &quot;</span>
    <span class="o">+</span> <span class="s2">&quot;{today.strftime,TEMPLATE} where TEMPLATE is a valid strftime template, e.g. &quot;</span>
    <span class="o">+</span> <span class="s2">&quot;{today.strftime,%Y-%U} would result in year-week number of year: &#39;2020-23&#39;. &quot;</span>
    <span class="o">+</span> <span class="s2">&quot;If used with no template will return null value. &quot;</span>
    <span class="o">+</span> <span class="s2">&quot;See https://strftime.org/ for help on strftime templates.&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{place.name}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;Place name from the photo&#39;s reverse geolocation data, as displayed in Photos&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{place.country_code}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;The ISO country code from the photo&#39;s reverse geolocation data&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{place.name.country}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;Country name from the photo&#39;s reverse geolocation data&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{place.name.state_province}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;State or province name from the photo&#39;s reverse geolocation data&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{place.name.city}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;City or locality name from the photo&#39;s reverse geolocation data&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{place.name.area_of_interest}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;Area of interest name (e.g. landmark or public place) from the photo&#39;s reverse geolocation data&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{place.address}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;Postal address from the photo&#39;s reverse geolocation data, e.g. &#39;2007 18th St NW, Washington, DC 20009, United States&#39;&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{place.address.street}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;Street part of the postal address, e.g. &#39;2007 18th St NW&#39;&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{place.address.city}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;City part of the postal address, e.g. &#39;Washington&#39;&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{place.address.state_province}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;State/province part of the postal address, e.g. &#39;DC&#39;&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{place.address.postal_code}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;Postal code part of the postal address, e.g. &#39;20009&#39;&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{place.address.country}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;Country name of the postal address, e.g. &#39;United States&#39;&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{place.address.country_code}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;ISO country code of the postal address, e.g. &#39;US&#39;&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{searchinfo.season}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;Season of the year associated with a photo, e.g. &#39;Summer&#39;; (Photos 5+ only, applied automatically by Photos&#39; image categorization algorithms).&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{exif.camera_make}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;Camera make from original photo&#39;s EXIF information as imported by Photos, e.g. &#39;Apple&#39;&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{exif.camera_model}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;Camera model from original photo&#39;s EXIF information as imported by Photos, e.g. &#39;iPhone 6s&#39;&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{exif.lens_model}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;Lens model from original photo&#39;s EXIF information as imported by Photos, e.g. &#39;iPhone 6s back camera 4.15mm f/2.2&#39;&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{moment}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;The moment title of the photo&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{uuid}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;Photo&#39;s internal universally unique identifier (UUID) for the photo, a 36-character string unique to the photo, e.g. &#39;128FB4C6-0B16-4E7D-9108-FB2E90DA1546&#39;&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{shortuuid}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;A shorter representation of photo&#39;s internal universally unique identifier (UUID) for the photo, &quot;</span>
    <span class="o">+</span> <span class="s2">&quot;a 22-character string unique to the photo, e.g. &#39;JYsxugP9UjetmCbBCHXcmu&#39;&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{id}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;A unique number for the photo based on its primary key in the Photos database. &quot;</span>
    <span class="o">+</span> <span class="s2">&quot;A sequential integer, e.g. 1, 2, 3...etc.  Each asset associated with a photo (e.g. an image and Live Photo preview) will share the same id. &quot;</span>
    <span class="o">+</span> <span class="s2">&quot;May be formatted using a python string format code. &quot;</span>
    <span class="o">+</span> <span class="s2">&quot;For example, to format as a 5-digit integer and pad with zeros, use &#39;</span><span class="si">{id:05d}</span><span class="s2">&#39; which results in &quot;</span>
    <span class="o">+</span> <span class="s2">&quot;00001, 00002, 00003...etc. &quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{counter}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;A sequential counter, starting at 0, that increments each time it is evaluated.&quot;</span>
    <span class="o">+</span> <span class="s2">&quot;To start counting at a value other than 0, append append &#39;(starting_value)&#39; to the field name.&quot;</span>
    <span class="o">+</span> <span class="s2">&quot;For example, to start counting at 1 instead of 0: &#39;{counter(1)}&#39;.&quot;</span>
    <span class="o">+</span> <span class="s2">&quot;May be formatted using a python string format code.&quot;</span>
    <span class="o">+</span> <span class="s2">&quot;For example, to format as a 5-digit integer and pad with zeros, use &#39;{counter:05d(1)}&#39;&quot;</span>
    <span class="o">+</span> <span class="s2">&quot;which results in 00001, 00002, 00003...etc.&quot;</span>
    <span class="o">+</span> <span class="s2">&quot;You may also specify a stop value which causes the counter to reset to the starting value&quot;</span>
    <span class="o">+</span> <span class="s2">&quot;when the stop value is reached and a step size which causes the counter to increment by&quot;</span>
    <span class="o">+</span> <span class="s2">&quot;the specified value instead of 1. Use the format &#39;{counter(start,stop,step)}&#39; where start,&quot;</span>
    <span class="o">+</span> <span class="s2">&quot;stop, and step are integers. For example, to count from 1 to 10 by 2, use &#39;{counter(1,11,2)}&#39;.&quot;</span>
    <span class="o">+</span> <span class="s2">&quot;Note that the counter stops counting when the stop value is reached and does not return the&quot;</span>
    <span class="o">+</span> <span class="s2">&quot;stop value. Start, stop, and step are optional and may be omitted. For example, to count&quot;</span>
    <span class="o">+</span> <span class="s2">&quot;from 0 by 2s, use &#39;{counter(,,2)}&#39;.&quot;</span>
    <span class="o">+</span> <span class="s2">&quot;You may create an arbitrary number of counters by appending a unique name to the field name&quot;</span>
    <span class="o">+</span> <span class="s2">&quot;preceded by a period: &#39;</span><span class="si">{counter.a}</span><span class="s2">&#39;, &#39;</span><span class="si">{counter.b}</span><span class="s2">&#39;, etc. Each counter will have its own state&quot;</span>
    <span class="o">+</span> <span class="s2">&quot;and will start at 0 and increment by 1 unless otherwise specified.&quot;</span>
    <span class="o">+</span> <span class="s2">&quot; Note: </span><span class="si">{counter}</span><span class="s2"> is not suitable for use with &#39;export&#39; and &#39;--update&#39; &quot;</span>
    <span class="o">+</span> <span class="s2">&quot;as the counter associated with a photo may change between export sessions. See also </span><span class="si">{id}</span><span class="s2">.&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{album_seq}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;An integer, starting at 0, indicating the photo&#39;s index (sequence) in the containing album. &quot;</span>
    <span class="o">+</span> <span class="s2">&quot;Only valid when used in a &#39;--filename&#39; template and only when &#39;</span><span class="si">{album}</span><span class="s2">&#39; or &#39;</span><span class="si">{folder_album}</span><span class="s2">&#39; is used in the &#39;--directory&#39; template. &quot;</span>
    <span class="o">+</span> <span class="s1">&#39;For example </span><span class="se">\&#39;</span><span class="s1">--directory &quot;</span><span class="si">{folder_album}</span><span class="s1">&quot; --filename &quot;</span><span class="si">{album_seq}</span><span class="s1">_</span><span class="si">{original_name}</span><span class="s1">&quot;</span><span class="se">\&#39;</span><span class="s1">. &#39;</span>
    <span class="o">+</span> <span class="s2">&quot;To start counting at a value other than 0, append append &#39;(starting_value)&#39; to the field name.  &quot;</span>
    <span class="o">+</span> <span class="s2">&quot;For example, to start counting at 1 instead of 0: &#39;{album_seq(1)}&#39;. &quot;</span>
    <span class="o">+</span> <span class="s2">&quot;May be formatted using a python string format code. &quot;</span>
    <span class="o">+</span> <span class="s2">&quot;For example, to format as a 5-digit integer and pad with zeros, use &#39;</span><span class="si">{album_seq:05d}</span><span class="s2">&#39; which results in &quot;</span>
    <span class="o">+</span> <span class="s2">&quot;00000, 00001, 00002...etc. &quot;</span>
    <span class="o">+</span> <span class="s2">&quot;To format while also using a starting value: &#39;{album_seq:05d(1)}&#39; which results in 0001, 00002...etc.&quot;</span>
    <span class="o">+</span> <span class="s2">&quot;This may result in incorrect sequences if you have duplicate albums with the same name; see also &#39;</span><span class="si">{folder_album_seq}</span><span class="s2">&#39;.&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{folder_album_seq}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;An integer, starting at 0, indicating the photo&#39;s index (sequence) in the containing album and folder path. &quot;</span>
    <span class="o">+</span> <span class="s2">&quot;Only valid when used in a &#39;--filename&#39; template and only when &#39;</span><span class="si">{folder_album}</span><span class="s2">&#39; is used in the &#39;--directory&#39; template. &quot;</span>
    <span class="o">+</span> <span class="s1">&#39;For example </span><span class="se">\&#39;</span><span class="s1">--directory &quot;</span><span class="si">{folder_album}</span><span class="s1">&quot; --filename &quot;</span><span class="si">{folder_album_seq}</span><span class="s1">_</span><span class="si">{original_name}</span><span class="s1">&quot;</span><span class="se">\&#39;</span><span class="s1">. &#39;</span>
    <span class="o">+</span> <span class="s2">&quot;To start counting at a value other than 0, append &#39;(starting_value)&#39; to the field name. &quot;</span>
    <span class="o">+</span> <span class="s2">&quot;For example, to start counting at 1 instead of 0: &#39;{folder_album_seq(1)}&#39; &quot;</span>
    <span class="o">+</span> <span class="s2">&quot;May be formatted using a python string format code. &quot;</span>
    <span class="o">+</span> <span class="s2">&quot;For example, to format as a 5-digit integer and pad with zeros, use &#39;</span><span class="si">{folder_album_seq:05d}</span><span class="s2">&#39; which results in &quot;</span>
    <span class="o">+</span> <span class="s2">&quot;00000, 00001, 00002...etc. &quot;</span>
    <span class="o">+</span> <span class="s2">&quot;To format while also using a starting value: &#39;{folder_album_seq:05d(1)}&#39; which results in 0001, 00002...etc.&quot;</span>
    <span class="o">+</span> <span class="s2">&quot;This may result in incorrect sequences if you have duplicate albums with the same name in the same folder; see also &#39;</span><span class="si">{album_seq}</span><span class="s2">&#39;. &quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{comma}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;A comma: &#39;,&#39;&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{semicolon}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;A semicolon: &#39;;&#39;&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{questionmark}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;A question mark: &#39;?&#39;&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{pipe}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;A vertical pipe: &#39;|&#39;&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{percent}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;A percent sign: &#39;%&#39;&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{ampersand}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;an ampersand symbol: &#39;&amp;&#39;&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{openbrace}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;An open brace: &#39;{&#39;&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{closebrace}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;A close brace: &#39;}&#39;&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{openparens}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;An open parentheses: &#39;(&#39;&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{closeparens}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;A close parentheses: &#39;)&#39;&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{openbracket}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;An open bracket: &#39;[&#39;&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{closebracket}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;A close bracket: &#39;]&#39;&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{newline}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="sa">r</span><span class="s2">&quot;A newline: &#39;\n&#39;&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{lf}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="sa">r</span><span class="s2">&quot;A line feed: &#39;\n&#39;, alias for </span><span class="si">{newline}</span><span class="s2">&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{cr}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="sa">r</span><span class="s2">&quot;A carriage return: &#39;\r&#39;&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{crlf}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="sa">r</span><span class="s2">&quot;A carriage return + line feed: &#39;\r\n&#39;&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{tab}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="sa">r</span><span class="s2">&quot;:A tab: &#39;\t&#39;&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{osxphotos_version}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="sa">f</span><span class="s2">&quot;The osxphotos version, e.g. &#39;</span><span class="si">{</span><span class="n">__version__</span><span class="si">}</span><span class="s2">&#39;&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{osxphotos_cmd_line}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;The full command line used to run osxphotos&quot;</span><span class="p">,</span>
<span class="p">}</span>

<span class="n">TEMPLATE_SUBSTITUTIONS_PATHLIB</span> <span class="o">=</span> <span class="p">{</span>
    <span class="s2">&quot;</span><span class="si">{export_dir}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;The full path to the export directory&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{filepath}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;The full path to the exported file&quot;</span><span class="p">,</span>
<span class="p">}</span>

<span class="c1"># Permitted multi-value substitutions (each of these returns None or 1 or more values)</span>
<span class="n">TEMPLATE_SUBSTITUTIONS_MULTI_VALUED</span> <span class="o">=</span> <span class="p">{</span>
    <span class="s2">&quot;</span><span class="si">{album}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;Album(s) photo is contained in&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{folder_album}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;Folder path + album photo is contained in. e.g. &#39;Folder/Subfolder/Album&#39; or just &#39;Album&#39; if no enclosing folder&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{project}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;Project(s) photo is contained in (such as greeting cards, calendars, slideshows)&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{album_project}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;Album(s) and project(s) photo is contained in; treats projects as regular albums&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{folder_album_project}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;Folder path + album (includes projects as albums) photo is contained in. e.g. &#39;Folder/Subfolder/Album&#39; or just &#39;Album&#39; if no enclosing folder&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{keyword}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;Keyword(s) assigned to photo&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{person}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;Person(s) / face(s) in a photo&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{label}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;Image categorization label associated with a photo (Photos 5+ only). &quot;</span>
    <span class="s2">&quot;Labels are added automatically by Photos using machine learning algorithms to categorize images. &quot;</span>
    <span class="s2">&quot;These are not the same as </span><span class="si">{keyword}</span><span class="s2"> which refers to the user-defined keywords/tags applied in Photos.&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{label_normalized}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;All lower case version of &#39;label&#39; (Photos 5+ only)&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{comment}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;Comment(s) on shared Photos; format is &#39;Person name: comment text&#39; (Photos 5+ only)&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{exiftool}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;Format: &#39;{exiftool:GROUP:TAGNAME}&#39;; use exiftool (https://exiftool.org) to extract metadata, in form GROUP:TAGNAME, from image.  &quot;</span>
    <span class="s2">&quot;E.g. &#39;{exiftool:EXIF:Make}&#39; to get camera make, or {exiftool:IPTC:Keywords} to extract keywords. &quot;</span>
    <span class="s2">&quot;See https://exiftool.org/TagNames/ for list of valid tag names.  You must specify group (e.g. EXIF, IPTC, etc) &quot;</span>
    <span class="s2">&quot;as used in &#39;exiftool -G&#39;. exiftool must be installed in the path to use this template.&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{searchinfo.holiday}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;Holiday names associated with a photo, e.g. &#39;Christmas Day&#39;; (Photos 5+ only, applied automatically by Photos&#39; image categorization algorithms).&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{searchinfo.activity}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;Activities associated with a photo, e.g. &#39;Sporting Event&#39;; (Photos 5+ only, applied automatically by Photos&#39; image categorization algorithms).&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{searchinfo.venue}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;Venues associated with a photo, e.g. name of restaurant; (Photos 5+ only, applied automatically by Photos&#39; image categorization algorithms).&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{searchinfo.venue_type}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;Venue types associated with a photo, e.g. &#39;Restaurant&#39;; (Photos 5+ only, applied automatically by Photos&#39; image categorization algorithms).&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{photo}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;Provides direct access to the PhotoInfo object for the photo. &quot;</span>
    <span class="o">+</span> <span class="s2">&quot;Must be used in format &#39;</span><span class="si">{photo.property}</span><span class="s2">&#39; where &#39;property&#39; represents a PhotoInfo property. &quot;</span>
    <span class="o">+</span> <span class="s2">&quot;For example: &#39;</span><span class="si">{photo.favorite}</span><span class="s2">&#39; is the same as &#39;</span><span class="si">{favorite}</span><span class="s2">&#39; and &#39;</span><span class="si">{photo.place.name}</span><span class="s2">&#39; is the same as &#39;</span><span class="si">{place.name}</span><span class="s2">&#39;. &quot;</span>
    <span class="o">+</span> <span class="s2">&quot;&#39;</span><span class="si">{photo}</span><span class="s2">&#39; provides access to properties that are not available as separate template fields but it assumes some knowledge of &quot;</span>
    <span class="o">+</span> <span class="s2">&quot;the underlying PhotoInfo class.  See https://rhettbull.github.io/osxphotos/ for additional documentation on the PhotoInfo class.&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{detected_text}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;List of text strings found in the image after performing text detection. &quot;</span>
    <span class="o">+</span> <span class="s2">&quot;Using &#39;</span><span class="si">{detected_text}</span><span class="s2">&#39; will cause osxphotos to perform text detection on your photos using the built-in macOS text detection algorithms which will slow down your export. &quot;</span>
    <span class="o">+</span> <span class="s2">&quot;The results for each photo will be cached in the export database so that future exports with &#39;--update&#39; do not need to reprocess each photo. &quot;</span>
    <span class="o">+</span> <span class="s2">&quot;You may pass a confidence threshold value between 0.0 and 1.0 after a colon as in &#39;</span><span class="si">{detected_text:0.5}</span><span class="s2">&#39;; &quot;</span>
    <span class="o">+</span> <span class="sa">f</span><span class="s2">&quot;The default confidence threshold is </span><span class="si">{</span><span class="n">TEXT_DETECTION_CONFIDENCE_THRESHOLD</span><span class="si">}</span><span class="s2">. &quot;</span>
    <span class="o">+</span> <span class="s2">&quot;&#39;</span><span class="si">{detected_text}</span><span class="s2">&#39; works only on macOS Catalina (10.15) or later. &quot;</span>
    <span class="o">+</span> <span class="s2">&quot;Note: this feature is not the same thing as Live Text in macOS Monterey, which osxphotos does not yet support.&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{shell_quote}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;Use in form &#39;{shell_quote,TEMPLATE}&#39;; quotes the rendered TEMPLATE value(s) for safe usage in the shell, e.g. My file.jpeg =&gt; &#39;My file.jpeg&#39;; only adds quotes if needed.&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{strip}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;Use in form &#39;{strip,TEMPLATE}&#39;; strips whitespace from begining and end of rendered TEMPLATE value(s).&quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{format}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;Use in form &#39;{format:TYPE:FORMAT,TEMPLATE}&#39;; converts TEMPLATE value to TYPE then formats the value &quot;</span>
    <span class="o">+</span> <span class="s2">&quot;using Python string formatting codes specified by FORMAT; TYPE is one of: &#39;int&#39;, &#39;float&#39;, or &#39;str&#39;. &quot;</span>
    <span class="s2">&quot;For example, &#39;{format:float:.1f,{exiftool:EXIF:FocalLength}}&#39; will format focal length to 1 decimal place (e.g. &#39;100.0&#39;). &quot;</span><span class="p">,</span>
    <span class="s2">&quot;</span><span class="si">{function}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="s2">&quot;Execute a python function from an external file and use return value as template substitution. &quot;</span>
    <span class="o">+</span> <span class="s2">&quot;Use in format: {function:file.py::function_name} where &#39;file.py&#39; is the path/name of the python file and &#39;function_name&#39; is the name of the function to call. &quot;</span>
    <span class="o">+</span> <span class="s2">&quot;The file name may also be url to a python file, e.g. &#39;{function:https://raw.githubusercontent.com/RhetTbull/osxphotos/main/examples/template_function.py::example}&#39;. &quot;</span>
    <span class="o">+</span> <span class="s2">&quot;The function will be passed the PhotoInfo object for the photo. &quot;</span>
    <span class="o">+</span> <span class="s2">&quot;See https://github.com/RhetTbull/osxphotos/blob/master/examples/template_function.py for an example of how to implement a template function.&quot;</span><span class="p">,</span>
<span class="p">}</span>

<span class="n">FILTER_VALUES</span> <span class="o">=</span> <span class="p">{</span>
    <span class="s2">&quot;lower&quot;</span><span class="p">:</span> <span class="s2">&quot;Convert value to lower case, e.g. &#39;Value&#39; =&gt; &#39;value&#39;.&quot;</span><span class="p">,</span>
    <span class="s2">&quot;upper&quot;</span><span class="p">:</span> <span class="s2">&quot;Convert value to upper case, e.g. &#39;Value&#39; =&gt; &#39;VALUE&#39;.&quot;</span><span class="p">,</span>
    <span class="s2">&quot;strip&quot;</span><span class="p">:</span> <span class="s2">&quot;Strip whitespace from beginning/end of value, e.g. &#39; Value &#39; =&gt; &#39;Value&#39;.&quot;</span><span class="p">,</span>
    <span class="s2">&quot;titlecase&quot;</span><span class="p">:</span> <span class="s2">&quot;Convert value to title case, e.g. &#39;my value&#39; =&gt; &#39;My Value&#39;.&quot;</span><span class="p">,</span>
    <span class="s2">&quot;capitalize&quot;</span><span class="p">:</span> <span class="s2">&quot;Capitalize first word of value and convert other words to lower case, e.g. &#39;MY VALUE&#39; =&gt; &#39;My value&#39;.&quot;</span><span class="p">,</span>
    <span class="s2">&quot;braces&quot;</span><span class="p">:</span> <span class="s2">&quot;Enclose value in curly braces, e.g. &#39;value =&gt; &#39;</span><span class="si">{value}</span><span class="s2">&#39;.&quot;</span><span class="p">,</span>
    <span class="s2">&quot;parens&quot;</span><span class="p">:</span> <span class="s2">&quot;Enclose value in parentheses, e.g. &#39;value&#39; =&gt; &#39;(value&#39;)&quot;</span><span class="p">,</span>
    <span class="s2">&quot;brackets&quot;</span><span class="p">:</span> <span class="s2">&quot;Enclose value in brackets, e.g. &#39;value&#39; =&gt; &#39;[value]&#39;&quot;</span><span class="p">,</span>
    <span class="s2">&quot;shell_quote&quot;</span><span class="p">:</span> <span class="s2">&quot;Quotes the value for safe usage in the shell, e.g. My file.jpeg =&gt; &#39;My file.jpeg&#39;; only adds quotes if needed.&quot;</span><span class="p">,</span>
    <span class="s2">&quot;function&quot;</span><span class="p">:</span> <span class="s2">&quot;Run custom python function to filter value; use in format &#39;function:/path/to/file.py::function_name&#39;. See example at https://github.com/RhetTbull/osxphotos/blob/master/examples/template_filter.py&quot;</span><span class="p">,</span>
    <span class="s2">&quot;split(x)&quot;</span><span class="p">:</span> <span class="s2">&quot;Split value into a list of values using x as delimiter, e.g. &#39;value1;value2&#39; =&gt; [&#39;value1&#39;, &#39;value2&#39;] if used with split(;).&quot;</span><span class="p">,</span>
    <span class="s2">&quot;autosplit&quot;</span><span class="p">:</span> <span class="s2">&quot;Automatically split delimited string into separate values; will split strings delimited by comma, semicolon, or space, e.g. &#39;value1,value2&#39; =&gt; [&#39;value1&#39;, &#39;value2&#39;].&quot;</span><span class="p">,</span>
    <span class="s2">&quot;chop(x)&quot;</span><span class="p">:</span> <span class="s2">&quot;Remove x characters off the end of value, e.g. chop(1): &#39;Value&#39; =&gt; &#39;Valu&#39;; when applied to a list, chops characters from each list value, e.g. chop(1): [&#39;travel&#39;, &#39;beach&#39;]=&gt; [&#39;trave&#39;, &#39;beac&#39;].&quot;</span><span class="p">,</span>
    <span class="s2">&quot;chomp(x)&quot;</span><span class="p">:</span> <span class="s2">&quot;Remove x characters from the beginning of value, e.g. chomp(1): [&#39;Value&#39;] =&gt; [&#39;alue&#39;]; when applied to a list, removes characters from each list value, e.g. chomp(1): [&#39;travel&#39;, &#39;beach&#39;]=&gt; [&#39;ravel&#39;, &#39;each&#39;].&quot;</span><span class="p">,</span>
    <span class="s2">&quot;sort&quot;</span><span class="p">:</span> <span class="s2">&quot;Sort list of values, e.g. [&#39;c&#39;, &#39;b&#39;, &#39;a&#39;] =&gt; [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;].&quot;</span><span class="p">,</span>
    <span class="s2">&quot;rsort&quot;</span><span class="p">:</span> <span class="s2">&quot;Sort list of values in reverse order, e.g. [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;] =&gt; [&#39;c&#39;, &#39;b&#39;, &#39;a&#39;].&quot;</span><span class="p">,</span>
    <span class="s2">&quot;reverse&quot;</span><span class="p">:</span> <span class="s2">&quot;Reverse order of values, e.g. [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;] =&gt; [&#39;c&#39;, &#39;b&#39;, &#39;a&#39;].&quot;</span><span class="p">,</span>
    <span class="s2">&quot;uniq&quot;</span><span class="p">:</span> <span class="s2">&quot;Remove duplicate values, e.g. [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;, &#39;b&#39;, &#39;a&#39;] =&gt; [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;].&quot;</span><span class="p">,</span>
    <span class="s2">&quot;join(x)&quot;</span><span class="p">:</span> <span class="s2">&quot;Join list of values with delimiter x, e.g. join(,): [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;] =&gt; &#39;a,b,c&#39;; &quot;</span>
    <span class="o">+</span> <span class="s2">&quot;the DELIM option functions similar to join(x) but with DELIM, the join happens before being passed to any filters.&quot;</span>
    <span class="o">+</span> <span class="s2">&quot;May optionally be used without an argument, that is &#39;join()&#39; which joins values together with no delimiter. &quot;</span>
    <span class="o">+</span> <span class="s2">&quot;e.g. join(): [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;] =&gt; &#39;abc&#39;.&quot;</span><span class="p">,</span>
    <span class="s2">&quot;append(x)&quot;</span><span class="p">:</span> <span class="s2">&quot;Append x to list of values, e.g. append(d): [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;] =&gt; [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;, &#39;d&#39;].&quot;</span><span class="p">,</span>
    <span class="s2">&quot;prepend(x)&quot;</span><span class="p">:</span> <span class="s2">&quot;Prepend x to list of values, e.g. prepend(d): [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;] =&gt; [&#39;d&#39;, &#39;a&#39;, &#39;b&#39;, &#39;c&#39;].&quot;</span><span class="p">,</span>
    <span class="s2">&quot;appends(x)&quot;</span><span class="p">:</span> <span class="s2">&quot;Append s[tring] Append x to each value of list of values, e.g. appends(d): [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;] =&gt; [&#39;ad&#39;, &#39;bd&#39;, &#39;cd&#39;].&quot;</span><span class="p">,</span>
    <span class="s2">&quot;prepends(x)&quot;</span><span class="p">:</span> <span class="s2">&quot;Prepend s[tring] x to each value of list of values, e.g. prepends(d): [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;] =&gt; [&#39;da&#39;, &#39;db&#39;, &#39;dc&#39;].&quot;</span><span class="p">,</span>
    <span class="s2">&quot;remove(x)&quot;</span><span class="p">:</span> <span class="s2">&quot;Remove x from list of values, e.g. remove(b): [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;] =&gt; [&#39;a&#39;, &#39;c&#39;].&quot;</span><span class="p">,</span>
    <span class="s2">&quot;slice(start:stop:step)&quot;</span><span class="p">:</span> <span class="s2">&quot;Slice list using same semantics as Python&#39;s list slicing, &quot;</span>
    <span class="o">+</span> <span class="s2">&quot;e.g. slice(1:3): [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;, &#39;d&#39;] =&gt; [&#39;b&#39;, &#39;c&#39;]; slice(1:4:2): [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;, &#39;d&#39;] =&gt; [&#39;b&#39;, &#39;d&#39;]; &quot;</span>
    <span class="o">+</span> <span class="s2">&quot;slice(1:): [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;, &#39;d&#39;] =&gt; [&#39;b&#39;, &#39;c&#39;, &#39;d&#39;]; slice(:-1): [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;, &#39;d&#39;] =&gt; [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;]; &quot;</span>
    <span class="o">+</span> <span class="s2">&quot;slice(::-1): [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;, &#39;d&#39;] =&gt; [&#39;d&#39;, &#39;c&#39;, &#39;b&#39;, &#39;a&#39;]. See also sslice().&quot;</span><span class="p">,</span>
    <span class="s2">&quot;sslice(start:stop:step)&quot;</span><span class="p">:</span> <span class="s2">&quot;[s(tring) slice] Slice values in a list using same semantics as Python&#39;s string slicing, &quot;</span>
    <span class="o">+</span> <span class="s2">&quot;e.g. sslice(1:3):&#39;abcd =&gt; &#39;bc&#39;; sslice(1:4:2): &#39;abcd&#39; =&gt; &#39;bd&#39;, etc. See also slice().&quot;</span><span class="p">,</span>
    <span class="s2">&quot;filter(x)&quot;</span><span class="p">:</span> <span class="s2">&quot;Filter list of values using predicate x; for example, &#39;{folder_album|filter(contains Events)}&#39; returns only folders/albums containing the word &#39;Events&#39; in their path.&quot;</span><span class="p">,</span>
    <span class="s2">&quot;int&quot;</span><span class="p">:</span> <span class="s2">&quot;Convert values in list to integer, e.g. 1.0 =&gt; 1. If value cannot be converted to integer, remove value from list. &quot;</span>
    <span class="o">+</span> <span class="s2">&quot;[&#39;1.1&#39;, &#39;x&#39;] =&gt; [&#39;1&#39;]. See also float.&quot;</span><span class="p">,</span>
    <span class="s2">&quot;float&quot;</span><span class="p">:</span> <span class="s2">&quot;Convert values in list to floating point number, e.g. 1 =&gt; 1.0. If value cannot be converted to float, remove value from list. &quot;</span>
    <span class="o">+</span> <span class="s2">&quot;[&#39;1&#39;, &#39;x&#39;] =&gt; [&#39;1.0&#39;]. See also int.&quot;</span><span class="p">,</span>
<span class="p">}</span>

<span class="c1"># Just the substitutions without the braces</span>
<span class="n">SINGLE_VALUE_SUBSTITUTIONS</span> <span class="o">=</span> <span class="p">[</span>
    <span class="n">field</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s2">&quot;{&quot;</span><span class="p">,</span> <span class="s2">&quot;&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s2">&quot;}&quot;</span><span class="p">,</span> <span class="s2">&quot;&quot;</span><span class="p">)</span> <span class="k">for</span> <span class="n">field</span> <span class="ow">in</span> <span class="n">TEMPLATE_SUBSTITUTIONS</span>
<span class="p">]</span>

<span class="n">PATHLIB_SUBSTITUTIONS</span> <span class="o">=</span> <span class="p">[</span>
    <span class="n">field</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s2">&quot;{&quot;</span><span class="p">,</span> <span class="s2">&quot;&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s2">&quot;}&quot;</span><span class="p">,</span> <span class="s2">&quot;&quot;</span><span class="p">)</span> <span class="k">for</span> <span class="n">field</span> <span class="ow">in</span> <span class="n">TEMPLATE_SUBSTITUTIONS_PATHLIB</span>
<span class="p">]</span>

<span class="n">MULTI_VALUE_SUBSTITUTIONS</span> <span class="o">=</span> <span class="p">[</span>
    <span class="n">field</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s2">&quot;{&quot;</span><span class="p">,</span> <span class="s2">&quot;&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s2">&quot;}&quot;</span><span class="p">,</span> <span class="s2">&quot;&quot;</span><span class="p">)</span>
    <span class="k">for</span> <span class="n">field</span> <span class="ow">in</span> <span class="n">TEMPLATE_SUBSTITUTIONS_MULTI_VALUED</span>
<span class="p">]</span>

<span class="n">FIELD_NAMES</span> <span class="o">=</span> <span class="p">(</span>
    <span class="n">SINGLE_VALUE_SUBSTITUTIONS</span> <span class="o">+</span> <span class="n">MULTI_VALUE_SUBSTITUTIONS</span> <span class="o">+</span> <span class="n">PATHLIB_SUBSTITUTIONS</span>
<span class="p">)</span>

<span class="c1"># default values for string manipulation template options</span>
<span class="n">INPLACE_DEFAULT</span> <span class="o">=</span> <span class="s2">&quot;,&quot;</span>
<span class="n">PATH_SEP_DEFAULT</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">sep</span>

<span class="c1"># globals for tracking {seq} substitutions</span>
<span class="n">_global_seq_count</span> <span class="o">=</span> <span class="mi">0</span>

<span class="n">PUNCTUATION</span> <span class="o">=</span> <span class="p">{</span>
    <span class="s2">&quot;comma&quot;</span><span class="p">:</span> <span class="s2">&quot;,&quot;</span><span class="p">,</span>
    <span class="s2">&quot;semicolon&quot;</span><span class="p">:</span> <span class="s2">&quot;;&quot;</span><span class="p">,</span>
    <span class="s2">&quot;pipe&quot;</span><span class="p">:</span> <span class="s2">&quot;|&quot;</span><span class="p">,</span>
    <span class="s2">&quot;percent&quot;</span><span class="p">:</span> <span class="s2">&quot;%&quot;</span><span class="p">,</span>
    <span class="s2">&quot;ampersand&quot;</span><span class="p">:</span> <span class="s2">&quot;&amp;&quot;</span><span class="p">,</span>
    <span class="s2">&quot;openbrace&quot;</span><span class="p">:</span> <span class="s2">&quot;{&quot;</span><span class="p">,</span>
    <span class="s2">&quot;closebrace&quot;</span><span class="p">:</span> <span class="s2">&quot;}&quot;</span><span class="p">,</span>
    <span class="s2">&quot;openparens&quot;</span><span class="p">:</span> <span class="s2">&quot;(&quot;</span><span class="p">,</span>
    <span class="s2">&quot;closeparens&quot;</span><span class="p">:</span> <span class="s2">&quot;)&quot;</span><span class="p">,</span>
    <span class="s2">&quot;openbracket&quot;</span><span class="p">:</span> <span class="s2">&quot;[&quot;</span><span class="p">,</span>
    <span class="s2">&quot;closebracket&quot;</span><span class="p">:</span> <span class="s2">&quot;]&quot;</span><span class="p">,</span>
    <span class="s2">&quot;questionmark&quot;</span><span class="p">:</span> <span class="s2">&quot;?&quot;</span><span class="p">,</span>
    <span class="s2">&quot;newline&quot;</span><span class="p">:</span> <span class="s2">&quot;</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">,</span>
    <span class="s2">&quot;lf&quot;</span><span class="p">:</span> <span class="s2">&quot;</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">,</span>
    <span class="s2">&quot;cr&quot;</span><span class="p">:</span> <span class="s2">&quot;</span><span class="se">\r</span><span class="s2">&quot;</span><span class="p">,</span>
    <span class="s2">&quot;crlf&quot;</span><span class="p">:</span> <span class="s2">&quot;</span><span class="se">\r\n</span><span class="s2">&quot;</span><span class="p">,</span>
    <span class="s2">&quot;tab&quot;</span><span class="p">:</span> <span class="s2">&quot;</span><span class="se">\t</span><span class="s2">&quot;</span><span class="p">,</span>
<span class="p">}</span>


<span class="nd">@dataclass</span>
<span class="k">class</span><span class="w"> </span><span class="nc">RenderOptions</span><span class="p">:</span>
<span class="w">    </span><span class="sd">&quot;&quot;&quot;Options for PhotoTemplate.render</span>

<span class="sd">    template: str template</span>
<span class="sd">    none_str: str to use default for None values, default is &#39;_&#39;</span>
<span class="sd">    path_sep: optional string to use as path separator, default is os.path.sep</span>
<span class="sd">    expand_inplace: expand multi-valued substitutions in-place as a single string</span>
<span class="sd">        instead of returning individual strings</span>
<span class="sd">    inplace_sep: optional string to use as separator between multi-valued keywords</span>
<span class="sd">    with expand_inplace; default is &#39;,&#39;</span>
<span class="sd">    filename: if True, template output will be sanitized to produce valid file name</span>
<span class="sd">    dirname: if True, template output will be sanitized to produce valid directory name</span>
<span class="sd">    strip: if True, strips leading/trailing whitespace from rendered templates</span>
<span class="sd">    edited_version: set to True if you want {edited_version} to resolve to True (e.g. exporting edited version of photo)</span>
<span class="sd">    export_dir: set to the export directory if you want to evaluate {export_dir} template</span>
<span class="sd">    dest_path: set to the destination path of the photo (for use by {function} template), only valid with --filename</span>
<span class="sd">    filepath: set to value for filepath of the exported photo if you want to evaluate {filepath} template</span>
<span class="sd">    quote: quote path templates for execution in the shell</span>
<span class="sd">    caller: which command is calling the template (e.g. &#39;export&#39;)</span>
<span class="sd">    &quot;&quot;&quot;</span>

    <span class="n">none_str</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">&quot;_&quot;</span>
    <span class="n">path_sep</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="n">PATH_SEP_DEFAULT</span>
    <span class="n">expand_inplace</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">False</span>
    <span class="n">inplace_sep</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="n">INPLACE_DEFAULT</span>
    <span class="n">filename</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">False</span>
    <span class="n">dirname</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">False</span>
    <span class="n">strip</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">False</span>
    <span class="n">edited_version</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">False</span>
    <span class="n">export_dir</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
    <span class="n">dest_path</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
    <span class="n">filepath</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
    <span class="n">quote</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">False</span>
    <span class="n">caller</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">&quot;export&quot;</span>


<span class="k">class</span><span class="w"> </span><span class="nc">PhotoTemplateParser</span><span class="p">:</span>
<span class="w">    </span><span class="sd">&quot;&quot;&quot;Parser for PhotoTemplate&quot;&quot;&quot;</span>

    <span class="c1"># implemented as Singleton</span>

    <span class="k">def</span><span class="w"> </span><span class="fm">__new__</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="w">        </span><span class="sd">&quot;&quot;&quot;create new object or return instance of already created singleton&quot;&quot;&quot;</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="nb">hasattr</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="s2">&quot;instance&quot;</span><span class="p">)</span> <span class="ow">or</span> <span class="ow">not</span> <span class="bp">cls</span><span class="o">.</span><span class="n">instance</span><span class="p">:</span>
            <span class="bp">cls</span><span class="o">.</span><span class="n">instance</span> <span class="o">=</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__new__</span><span class="p">(</span><span class="bp">cls</span><span class="p">)</span>

        <span class="k">return</span> <span class="bp">cls</span><span class="o">.</span><span class="n">instance</span>

    <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w">        </span><span class="sd">&quot;&quot;&quot;return existing singleton or create a new one&quot;&quot;&quot;</span>

        <span class="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="s2">&quot;metamodel&quot;</span><span class="p">):</span>
            <span class="k">return</span>

        <span class="bp">self</span><span class="o">.</span><span class="n">metamodel</span> <span class="o">=</span> <span class="n">metamodel_from_file</span><span class="p">(</span><span class="n">MTL_GRAMMAR_MODEL</span><span class="p">,</span> <span class="n">skipws</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>

    <span class="k">def</span><span class="w"> </span><span class="nf">parse</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">template_statement</span><span class="p">):</span>
<span class="w">        </span><span class="sd">&quot;&quot;&quot;Parse a template_statement string&quot;&quot;&quot;</span>
        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">metamodel</span><span class="o">.</span><span class="n">model_from_str</span><span class="p">(</span><span class="n">template_statement</span><span class="p">)</span>

    <span class="k">def</span><span class="w"> </span><span class="nf">fields</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">template_statement</span><span class="p">):</span>
<span class="w">        </span><span class="sd">&quot;&quot;&quot;Return list of fields found in a template statement; does not verify that fields are valid&quot;&quot;&quot;</span>
        <span class="n">model</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="n">template_statement</span><span class="p">)</span>
        <span class="k">return</span> <span class="p">[</span><span class="n">ts</span><span class="o">.</span><span class="n">template</span><span class="o">.</span><span class="n">field</span> <span class="k">for</span> <span class="n">ts</span> <span class="ow">in</span> <span class="n">model</span><span class="o">.</span><span class="n">template_strings</span> <span class="k">if</span> <span class="n">ts</span><span class="o">.</span><span class="n">template</span><span class="p">]</span>


<div class="viewcode-block" id="PhotoTemplate">
<a class="viewcode-back" href="../../reference.html#osxphotos.PhotoTemplate">[docs]</a>
<span class="k">class</span><span class="w"> </span><span class="nc">PhotoTemplate</span><span class="p">:</span>
<span class="w">    </span><span class="sd">&quot;&quot;&quot;PhotoTemplate class to render a template string from a PhotoInfo object&quot;&quot;&quot;</span>

    <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">photo</span><span class="p">,</span> <span class="n">exiftool_path</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="w">        </span><span class="sd">&quot;&quot;&quot;Inits PhotoTemplate class with photo</span>

<span class="sd">        Args:</span>
<span class="sd">            photo: a PhotoInfo instance.</span>
<span class="sd">            exiftool_path: optional path to exiftool for use with {exiftool:} template; if not provided, will look for exiftool in $PATH</span>
<span class="sd">        &quot;&quot;&quot;</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">photo</span> <span class="o">=</span> <span class="n">photo</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">exiftool_path</span> <span class="o">=</span> <span class="n">exiftool_path</span>

        <span class="c1"># holds value of current date/time for {today.x} fields</span>
        <span class="c1"># gets initialized in get_template_value</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">today</span> <span class="o">=</span> <span class="kc">None</span>

        <span class="c1"># get parser singleton</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">parser</span> <span class="o">=</span> <span class="n">PhotoTemplateParser</span><span class="p">()</span>

        <span class="c1"># initialize render options</span>
        <span class="c1"># this will be done in render() but for testing, some of the lookup functions are called directly</span>
        <span class="n">options</span> <span class="o">=</span> <span class="n">RenderOptions</span><span class="p">()</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="o">=</span> <span class="n">options</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">path_sep</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">path_sep</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">inplace_sep</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">inplace_sep</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">edited_version</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">edited_version</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">none_str</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">none_str</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">expand_inplace</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">expand_inplace</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">filename</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">filename</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">dirname</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">dirname</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">strip</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">strip</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">export_dir</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">export_dir</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">filepath</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">filepath</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">quote</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">quote</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">dest_path</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">dest_path</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">variables</span> <span class="o">=</span> <span class="p">{}</span>

<div class="viewcode-block" id="PhotoTemplate.render">
<a class="viewcode-back" href="../../reference.html#osxphotos.PhotoTemplate.render">[docs]</a>
    <span class="k">def</span><span class="w"> </span><span class="nf">render</span><span class="p">(</span>
        <span class="bp">self</span><span class="p">,</span>
        <span class="n">template</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
        <span class="n">options</span><span class="p">:</span> <span class="n">RenderOptions</span><span class="p">,</span>
    <span class="p">):</span>
<span class="w">        </span><span class="sd">&quot;&quot;&quot;Render a filename or directory template</span>

<span class="sd">        Args:</span>
<span class="sd">            template: str template</span>
<span class="sd">            options: a RenderOptions instance</span>

<span class="sd">        Returns:</span>
<span class="sd">            ([rendered_strings], [unmatched]): tuple of list of rendered strings and list of unmatched template values</span>
<span class="sd">        &quot;&quot;&quot;</span>

        <span class="k">if</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">template</span><span class="p">,</span> <span class="nb">str</span><span class="p">):</span>
            <span class="k">raise</span> <span class="ne">TypeError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;template must be type str, not </span><span class="si">{</span><span class="nb">type</span><span class="p">(</span><span class="n">template</span><span class="p">)</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>

        <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="o">=</span> <span class="n">options</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">path_sep</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">path_sep</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">inplace_sep</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">inplace_sep</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">edited_version</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">edited_version</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">none_str</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">none_str</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">expand_inplace</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">expand_inplace</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">filename</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">filename</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">dirname</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">dirname</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">strip</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">strip</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">export_dir</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">export_dir</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">dest_path</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">dest_path</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">filepath</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">filepath</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">quote</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">quote</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">dest_path</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">dest_path</span>

        <span class="k">try</span><span class="p">:</span>
            <span class="n">model</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">parser</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="n">template</span><span class="p">)</span>
        <span class="k">except</span> <span class="n">TextXSyntaxError</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
            <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;SyntaxError: </span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span> <span class="kn">from</span><span class="w"> </span><span class="nn">e</span>

        <span class="k">if</span> <span class="ow">not</span> <span class="n">model</span><span class="p">:</span>
            <span class="c1"># empty string</span>
            <span class="k">return</span> <span class="p">[],</span> <span class="p">[]</span>

        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_render_statement</span><span class="p">(</span><span class="n">model</span><span class="p">)</span></div>


    <span class="k">def</span><span class="w"> </span><span class="nf">_render_statement</span><span class="p">(</span>
        <span class="bp">self</span><span class="p">,</span>
        <span class="n">statement</span><span class="p">,</span>
        <span class="n">field_arg</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
    <span class="p">):</span>
        <span class="n">results</span> <span class="o">=</span> <span class="p">[]</span>
        <span class="n">unmatched</span> <span class="o">=</span> <span class="p">[]</span>
        <span class="k">for</span> <span class="n">ts</span> <span class="ow">in</span> <span class="n">statement</span><span class="o">.</span><span class="n">template_strings</span><span class="p">:</span>
            <span class="n">results</span><span class="p">,</span> <span class="n">unmatched</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_render_template_string</span><span class="p">(</span>
                <span class="n">ts</span><span class="p">,</span> <span class="n">results</span><span class="o">=</span><span class="n">results</span><span class="p">,</span> <span class="n">unmatched</span><span class="o">=</span><span class="n">unmatched</span><span class="p">,</span> <span class="n">field_arg</span><span class="o">=</span><span class="n">field_arg</span>
            <span class="p">)</span>

        <span class="n">rendered_strings</span> <span class="o">=</span> <span class="n">results</span>

        <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">filename</span><span class="p">:</span>
            <span class="n">rendered_strings</span> <span class="o">=</span> <span class="p">[</span>
                <span class="n">sanitize_filename</span><span class="p">(</span><span class="n">rendered_str</span><span class="p">)</span> <span class="k">for</span> <span class="n">rendered_str</span> <span class="ow">in</span> <span class="n">rendered_strings</span>
            <span class="p">]</span>

        <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">strip</span><span class="p">:</span>
            <span class="n">rendered_strings</span> <span class="o">=</span> <span class="p">[</span>
                <span class="n">rendered_str</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span> <span class="k">for</span> <span class="n">rendered_str</span> <span class="ow">in</span> <span class="n">rendered_strings</span>
            <span class="p">]</span>

        <span class="k">return</span> <span class="n">rendered_strings</span><span class="p">,</span> <span class="n">unmatched</span>

    <span class="k">def</span><span class="w"> </span><span class="nf">_render_template_string</span><span class="p">(</span>
        <span class="bp">self</span><span class="p">,</span>
        <span class="n">ts</span><span class="p">,</span>
        <span class="n">field_arg</span><span class="p">,</span>
        <span class="n">results</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
        <span class="n">unmatched</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
    <span class="p">):</span>
<span class="w">        </span><span class="sd">&quot;&quot;&quot;Render a TemplateString object&quot;&quot;&quot;</span>

        <span class="n">results</span> <span class="o">=</span> <span class="n">results</span> <span class="ow">or</span> <span class="p">[</span><span class="s2">&quot;&quot;</span><span class="p">]</span>
        <span class="n">unmatched</span> <span class="o">=</span> <span class="n">unmatched</span> <span class="ow">or</span> <span class="p">[]</span>

        <span class="k">if</span> <span class="n">ts</span><span class="o">.</span><span class="n">template</span><span class="p">:</span>
            <span class="c1"># have a template field to process</span>
            <span class="n">field</span> <span class="o">=</span> <span class="n">ts</span><span class="o">.</span><span class="n">template</span><span class="o">.</span><span class="n">field</span>
            <span class="n">subfield</span> <span class="o">=</span> <span class="n">ts</span><span class="o">.</span><span class="n">template</span><span class="o">.</span><span class="n">subfield</span>

            <span class="c1"># process filters</span>
            <span class="n">filters</span> <span class="o">=</span> <span class="p">[]</span>
            <span class="k">if</span> <span class="n">ts</span><span class="o">.</span><span class="n">template</span><span class="o">.</span><span class="n">filter</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
                <span class="n">filters</span> <span class="o">=</span> <span class="n">ts</span><span class="o">.</span><span class="n">template</span><span class="o">.</span><span class="n">filter</span><span class="o">.</span><span class="n">value</span>

            <span class="c1"># process field arguments</span>
            <span class="k">if</span> <span class="n">ts</span><span class="o">.</span><span class="n">template</span><span class="o">.</span><span class="n">fieldarg</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
                <span class="n">field_arg</span> <span class="o">=</span> <span class="n">ts</span><span class="o">.</span><span class="n">template</span><span class="o">.</span><span class="n">fieldarg</span><span class="o">.</span><span class="n">value</span>
            <span class="k">else</span><span class="p">:</span>
                <span class="n">field_arg</span> <span class="o">=</span> <span class="kc">None</span>

            <span class="c1"># process delim</span>
            <span class="k">if</span> <span class="n">ts</span><span class="o">.</span><span class="n">template</span><span class="o">.</span><span class="n">delim</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
                <span class="c1"># if value is None, means format was {+field}</span>
                <span class="n">delim</span> <span class="o">=</span> <span class="n">ts</span><span class="o">.</span><span class="n">template</span><span class="o">.</span><span class="n">delim</span><span class="o">.</span><span class="n">value</span> <span class="ow">or</span> <span class="s2">&quot;&quot;</span>
                <span class="n">delim</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">expand_variables_to_str</span><span class="p">(</span><span class="n">delim</span><span class="p">,</span> <span class="s2">&quot;delim&quot;</span><span class="p">)</span>
            <span class="k">else</span><span class="p">:</span>
                <span class="n">delim</span> <span class="o">=</span> <span class="kc">None</span>

            <span class="c1"># process combine operator</span>
            <span class="k">if</span> <span class="n">ts</span><span class="o">.</span><span class="n">template</span><span class="o">.</span><span class="n">combine</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
                <span class="n">is_combine</span> <span class="o">=</span> <span class="kc">True</span>
                <span class="k">if</span> <span class="n">ts</span><span class="o">.</span><span class="n">template</span><span class="o">.</span><span class="n">combine</span><span class="o">.</span><span class="n">value</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
                    <span class="n">combine_val</span><span class="p">,</span> <span class="n">u</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_render_statement</span><span class="p">(</span>
                        <span class="n">ts</span><span class="o">.</span><span class="n">template</span><span class="o">.</span><span class="n">combine</span><span class="o">.</span><span class="n">value</span><span class="p">,</span>
                        <span class="n">field_arg</span><span class="o">=</span><span class="n">field_arg</span><span class="p">,</span>
                    <span class="p">)</span>
                    <span class="n">unmatched</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">u</span><span class="p">)</span>
                <span class="k">else</span><span class="p">:</span>
                    <span class="c1"># blank combine value</span>
                    <span class="n">combine_val</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;&quot;</span><span class="p">]</span>
            <span class="k">else</span><span class="p">:</span>
                <span class="n">is_combine</span> <span class="o">=</span> <span class="kc">False</span>
                <span class="n">combine_val</span> <span class="o">=</span> <span class="kc">None</span>

            <span class="c1"># process boolean operator</span>
            <span class="k">if</span> <span class="n">ts</span><span class="o">.</span><span class="n">template</span><span class="o">.</span><span class="n">bool</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
                <span class="n">is_bool</span> <span class="o">=</span> <span class="kc">True</span>
                <span class="k">if</span> <span class="n">ts</span><span class="o">.</span><span class="n">template</span><span class="o">.</span><span class="n">bool</span><span class="o">.</span><span class="n">value</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
                    <span class="n">bool_val</span><span class="p">,</span> <span class="n">u</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_render_statement</span><span class="p">(</span>
                        <span class="n">ts</span><span class="o">.</span><span class="n">template</span><span class="o">.</span><span class="n">bool</span><span class="o">.</span><span class="n">value</span><span class="p">,</span>
                        <span class="n">field_arg</span><span class="o">=</span><span class="n">field_arg</span><span class="p">,</span>
                    <span class="p">)</span>
                    <span class="n">unmatched</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">u</span><span class="p">)</span>
                <span class="k">else</span><span class="p">:</span>
                    <span class="c1"># blank bool value</span>
                    <span class="n">bool_val</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;&quot;</span><span class="p">]</span>
            <span class="k">else</span><span class="p">:</span>
                <span class="n">is_bool</span> <span class="o">=</span> <span class="kc">False</span>
                <span class="n">bool_val</span> <span class="o">=</span> <span class="kc">None</span>

            <span class="c1"># process default</span>
            <span class="k">if</span> <span class="n">ts</span><span class="o">.</span><span class="n">template</span><span class="o">.</span><span class="n">default</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
                <span class="c1"># default is also a TemplateString</span>
                <span class="k">if</span> <span class="n">ts</span><span class="o">.</span><span class="n">template</span><span class="o">.</span><span class="n">default</span><span class="o">.</span><span class="n">value</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
                    <span class="n">default</span><span class="p">,</span> <span class="n">u</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_render_statement</span><span class="p">(</span>
                        <span class="n">ts</span><span class="o">.</span><span class="n">template</span><span class="o">.</span><span class="n">default</span><span class="o">.</span><span class="n">value</span><span class="p">,</span>
                        <span class="n">field_arg</span><span class="o">=</span><span class="n">field_arg</span><span class="p">,</span>
                    <span class="p">)</span>
                    <span class="n">unmatched</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">u</span><span class="p">)</span>
                <span class="k">else</span><span class="p">:</span>
                    <span class="c1"># blank default value</span>
                    <span class="n">default</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;&quot;</span><span class="p">]</span>
            <span class="k">else</span><span class="p">:</span>
                <span class="n">default</span> <span class="o">=</span> <span class="p">[]</span>

            <span class="c1"># process conditional</span>
            <span class="k">if</span> <span class="n">ts</span><span class="o">.</span><span class="n">template</span><span class="o">.</span><span class="n">conditional</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
                <span class="n">operator</span> <span class="o">=</span> <span class="n">ts</span><span class="o">.</span><span class="n">template</span><span class="o">.</span><span class="n">conditional</span><span class="o">.</span><span class="n">operator</span>
                <span class="n">negation</span> <span class="o">=</span> <span class="n">ts</span><span class="o">.</span><span class="n">template</span><span class="o">.</span><span class="n">conditional</span><span class="o">.</span><span class="n">negation</span>
                <span class="k">if</span> <span class="n">ts</span><span class="o">.</span><span class="n">template</span><span class="o">.</span><span class="n">conditional</span><span class="o">.</span><span class="n">value</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
                    <span class="c1"># conditional value is also a TemplateString</span>
                    <span class="n">conditional_value</span> <span class="o">=</span> <span class="p">[]</span>
                    <span class="k">for</span> <span class="n">cv</span> <span class="ow">in</span> <span class="n">ts</span><span class="o">.</span><span class="n">template</span><span class="o">.</span><span class="n">conditional</span><span class="o">.</span><span class="n">value</span><span class="p">:</span>
                        <span class="n">value</span><span class="p">,</span> <span class="n">u</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_render_statement</span><span class="p">(</span><span class="n">cv</span><span class="p">)</span>
                        <span class="n">conditional_value</span> <span class="o">+=</span> <span class="n">value</span>
                        <span class="n">unmatched</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">u</span><span class="p">)</span>
                <span class="k">else</span><span class="p">:</span>
                    <span class="c1"># this shouldn&#39;t happen</span>
                    <span class="n">conditional_value</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;&quot;</span><span class="p">]</span>
            <span class="k">else</span><span class="p">:</span>
                <span class="n">operator</span> <span class="o">=</span> <span class="kc">None</span>
                <span class="n">negation</span> <span class="o">=</span> <span class="kc">None</span>
                <span class="n">conditional_value</span> <span class="o">=</span> <span class="p">[]</span>

            <span class="k">if</span> <span class="n">field</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s2">&quot;%&quot;</span><span class="p">):</span>
                <span class="c1"># variable in form {%var}</span>
                <span class="n">vals</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">variables</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">field</span><span class="p">[</span><span class="mi">1</span><span class="p">:],</span> <span class="kc">None</span><span class="p">)</span>
                <span class="k">if</span> <span class="n">vals</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
                    <span class="k">raise</span> <span class="ne">SyntaxError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Variable &#39;</span><span class="si">{</span><span class="n">field</span><span class="p">[</span><span class="mi">1</span><span class="p">:]</span><span class="si">}</span><span class="s2">&#39; is not defined.&quot;</span><span class="p">)</span>
            <span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">&quot;var&quot;</span><span class="p">:</span>
                <span class="k">if</span> <span class="ow">not</span> <span class="n">subfield</span> <span class="ow">or</span> <span class="ow">not</span> <span class="n">default</span><span class="p">:</span>
                    <span class="k">raise</span> <span class="ne">SyntaxError</span><span class="p">(</span>
                        <span class="s2">&quot;var must have a subfield and value in form {var:subfield,value}&quot;</span>
                    <span class="p">)</span>
                <span class="c1"># strip empty string from default, #1318, #512</span>
                <span class="n">default</span> <span class="o">=</span> <span class="p">[</span><span class="n">d</span> <span class="k">for</span> <span class="n">d</span> <span class="ow">in</span> <span class="n">default</span> <span class="k">if</span> <span class="n">d</span> <span class="o">!=</span> <span class="s2">&quot;&quot;</span><span class="p">]</span>
                <span class="bp">self</span><span class="o">.</span><span class="n">variables</span><span class="p">[</span><span class="n">subfield</span><span class="p">]</span> <span class="o">=</span> <span class="n">default</span>
                <span class="n">vals</span> <span class="o">=</span> <span class="p">[]</span>
            <span class="k">else</span><span class="p">:</span>
                <span class="n">vals</span><span class="p">,</span> <span class="n">u</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_field_values</span><span class="p">(</span><span class="n">field</span><span class="p">,</span> <span class="n">subfield</span><span class="p">,</span> <span class="n">field_arg</span><span class="p">,</span> <span class="n">default</span><span class="p">)</span>
                <span class="k">if</span> <span class="n">u</span><span class="p">:</span>
                    <span class="n">unmatched</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">u</span><span class="p">)</span>
                    <span class="k">return</span> <span class="p">[],</span> <span class="n">unmatched</span>

            <span class="n">vals</span> <span class="o">=</span> <span class="p">[</span><span class="n">val</span> <span class="k">for</span> <span class="n">val</span> <span class="ow">in</span> <span class="n">vals</span> <span class="k">if</span> <span class="n">val</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">]</span>

            <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">expand_inplace</span> <span class="ow">or</span> <span class="n">delim</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
                <span class="n">sep</span> <span class="o">=</span> <span class="n">delim</span> <span class="k">if</span> <span class="n">delim</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="k">else</span> <span class="bp">self</span><span class="o">.</span><span class="n">inplace_sep</span>
                <span class="n">vals</span> <span class="o">=</span> <span class="p">[</span><span class="n">sep</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">vals</span><span class="p">)]</span> <span class="k">if</span> <span class="n">vals</span> <span class="k">else</span> <span class="p">[]</span>

            <span class="k">for</span> <span class="n">filter_</span> <span class="ow">in</span> <span class="n">filters</span><span class="p">:</span>
                <span class="n">vals</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_filter_values</span><span class="p">(</span><span class="n">filter_</span><span class="p">,</span> <span class="n">vals</span><span class="p">)</span>

            <span class="c1"># process find/replace</span>
            <span class="k">if</span> <span class="n">ts</span><span class="o">.</span><span class="n">template</span><span class="o">.</span><span class="n">findreplace</span><span class="p">:</span>
                <span class="n">new_vals</span> <span class="o">=</span> <span class="p">[]</span>
                <span class="k">for</span> <span class="n">val</span> <span class="ow">in</span> <span class="n">vals</span><span class="p">:</span>
                    <span class="k">for</span> <span class="n">pair</span> <span class="ow">in</span> <span class="n">ts</span><span class="o">.</span><span class="n">template</span><span class="o">.</span><span class="n">findreplace</span><span class="o">.</span><span class="n">pairs</span><span class="p">:</span>
                        <span class="n">find</span> <span class="o">=</span> <span class="n">pair</span><span class="o">.</span><span class="n">find</span> <span class="ow">or</span> <span class="s2">&quot;&quot;</span>
                        <span class="n">find</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">expand_variables_to_str</span><span class="p">(</span><span class="n">find</span><span class="p">,</span> <span class="s2">&quot;find/replace&quot;</span><span class="p">)</span>
                        <span class="n">repl</span> <span class="o">=</span> <span class="n">pair</span><span class="o">.</span><span class="n">replace</span> <span class="ow">or</span> <span class="s2">&quot;&quot;</span>
                        <span class="n">repl</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">expand_variables_to_str</span><span class="p">(</span><span class="n">repl</span><span class="p">,</span> <span class="s2">&quot;find/replace&quot;</span><span class="p">)</span>
                        <span class="n">val</span> <span class="o">=</span> <span class="n">val</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="n">find</span><span class="p">,</span> <span class="n">repl</span><span class="p">)</span>
                    <span class="n">new_vals</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">val</span><span class="p">)</span>
                <span class="n">vals</span> <span class="o">=</span> <span class="n">new_vals</span>

            <span class="k">if</span> <span class="n">operator</span><span class="p">:</span>
                <span class="c1"># have a conditional operator</span>

                <span class="k">def</span><span class="w"> </span><span class="nf">string_test</span><span class="p">(</span><span class="n">test_function</span><span class="p">):</span>
<span class="w">                    </span><span class="sd">&quot;&quot;&quot;Perform string comparison using test_function; closure to capture conditional_value, vals, negation&quot;&quot;&quot;</span>
                    <span class="n">match</span> <span class="o">=</span> <span class="kc">False</span>
                    <span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="n">conditional_value</span><span class="p">:</span>
                        <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">vals</span><span class="p">:</span>
                            <span class="k">if</span> <span class="n">test_function</span><span class="p">(</span><span class="n">v</span><span class="p">,</span> <span class="n">c</span><span class="p">):</span>
                                <span class="n">match</span> <span class="o">=</span> <span class="kc">True</span>
                                <span class="k">break</span>
                        <span class="k">if</span> <span class="n">match</span><span class="p">:</span>
                            <span class="k">break</span>

                    <span class="k">return</span> <span class="p">(</span>
                        <span class="p">[</span><span class="s2">&quot;True&quot;</span><span class="p">]</span>
                        <span class="k">if</span> <span class="p">(</span><span class="n">match</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">negation</span><span class="p">)</span> <span class="ow">or</span> <span class="p">(</span><span class="n">negation</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">match</span><span class="p">)</span>
                        <span class="k">else</span> <span class="p">[]</span>
                    <span class="p">)</span>

                <span class="k">def</span><span class="w"> </span><span class="nf">comparison_test</span><span class="p">(</span><span class="n">test_function</span><span class="p">):</span>
<span class="w">                    </span><span class="sd">&quot;&quot;&quot;Perform numerical comparisons using test_function; closure to capture conditional_val, vals, negation&quot;&quot;&quot;</span>
                    <span class="c1"># returns True if any of the values match the condition</span>
                    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">conditional_value</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">1</span><span class="p">:</span>
                        <span class="k">raise</span> <span class="ne">SyntaxError</span><span class="p">(</span>
                            <span class="sa">f</span><span class="s2">&quot;comparison operators may only be used with a single conditional value: </span><span class="si">{</span><span class="n">conditional_value</span><span class="si">}</span><span class="s2">&quot;</span>
                        <span class="p">)</span>
                    <span class="k">try</span><span class="p">:</span>
                        <span class="n">match</span> <span class="o">=</span> <span class="nb">any</span><span class="p">(</span>
                            <span class="nb">bool</span><span class="p">(</span><span class="n">test_function</span><span class="p">(</span><span class="nb">float</span><span class="p">(</span><span class="n">v</span><span class="p">),</span> <span class="nb">float</span><span class="p">(</span><span class="n">conditional_value</span><span class="p">[</span><span class="mi">0</span><span class="p">])))</span>
                            <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">vals</span>
                        <span class="p">)</span>
                        <span class="k">return</span> <span class="p">(</span>
                            <span class="p">[</span><span class="s2">&quot;True&quot;</span><span class="p">]</span>
                            <span class="k">if</span> <span class="p">(</span><span class="n">match</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">negation</span><span class="p">)</span> <span class="ow">or</span> <span class="p">(</span><span class="n">negation</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">match</span><span class="p">)</span>
                            <span class="k">else</span> <span class="p">[]</span>
                        <span class="p">)</span>
                    <span class="k">except</span> <span class="ne">ValueError</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
                        <span class="k">raise</span> <span class="ne">SyntaxError</span><span class="p">(</span>
                            <span class="sa">f</span><span class="s2">&quot;comparison operators may only be used with values that can be converted to numbers: </span><span class="si">{</span><span class="n">vals</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">conditional_value</span><span class="si">}</span><span class="s2">&quot;</span>
                        <span class="p">)</span> <span class="kn">from</span><span class="w"> </span><span class="nn">e</span>

                <span class="k">if</span> <span class="n">operator</span> <span class="ow">in</span> <span class="p">[</span><span class="s2">&quot;contains&quot;</span><span class="p">,</span> <span class="s2">&quot;matches&quot;</span><span class="p">,</span> <span class="s2">&quot;startswith&quot;</span><span class="p">,</span> <span class="s2">&quot;endswith&quot;</span><span class="p">]:</span>
                    <span class="c1"># process any &quot;or&quot; values separated by &quot;|&quot;</span>
                    <span class="n">temp_values</span> <span class="o">=</span> <span class="p">[]</span>
                    <span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="n">conditional_value</span><span class="p">:</span>
                        <span class="n">temp_values</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">c</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot;|&quot;</span><span class="p">))</span>
                    <span class="n">conditional_value</span> <span class="o">=</span> <span class="n">temp_values</span>

                <span class="k">if</span> <span class="n">operator</span> <span class="o">==</span> <span class="s2">&quot;contains&quot;</span><span class="p">:</span>
                    <span class="n">vals</span> <span class="o">=</span> <span class="n">string_test</span><span class="p">(</span><span class="k">lambda</span> <span class="n">v</span><span class="p">,</span> <span class="n">c</span><span class="p">:</span> <span class="n">c</span> <span class="ow">in</span> <span class="n">v</span><span class="p">)</span>
                <span class="k">elif</span> <span class="n">operator</span> <span class="o">==</span> <span class="s2">&quot;matches&quot;</span><span class="p">:</span>
                    <span class="n">vals</span> <span class="o">=</span> <span class="n">string_test</span><span class="p">(</span><span class="k">lambda</span> <span class="n">v</span><span class="p">,</span> <span class="n">c</span><span class="p">:</span> <span class="n">v</span> <span class="o">==</span> <span class="n">c</span><span class="p">)</span>
                <span class="k">elif</span> <span class="n">operator</span> <span class="o">==</span> <span class="s2">&quot;startswith&quot;</span><span class="p">:</span>
                    <span class="n">vals</span> <span class="o">=</span> <span class="n">string_test</span><span class="p">(</span><span class="k">lambda</span> <span class="n">v</span><span class="p">,</span> <span class="n">c</span><span class="p">:</span> <span class="n">v</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="n">c</span><span class="p">))</span>
                <span class="k">elif</span> <span class="n">operator</span> <span class="o">==</span> <span class="s2">&quot;endswith&quot;</span><span class="p">:</span>
                    <span class="n">vals</span> <span class="o">=</span> <span class="n">string_test</span><span class="p">(</span><span class="k">lambda</span> <span class="n">v</span><span class="p">,</span> <span class="n">c</span><span class="p">:</span> <span class="n">v</span><span class="o">.</span><span class="n">endswith</span><span class="p">(</span><span class="n">c</span><span class="p">))</span>
                <span class="k">elif</span> <span class="n">operator</span> <span class="o">==</span> <span class="s2">&quot;==&quot;</span><span class="p">:</span>
                    <span class="n">match</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">vals</span><span class="p">)</span> <span class="o">==</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">conditional_value</span><span class="p">)</span>
                    <span class="n">vals</span> <span class="o">=</span> <span class="p">(</span>
                        <span class="p">[</span><span class="s2">&quot;True&quot;</span><span class="p">]</span>
                        <span class="k">if</span> <span class="p">(</span><span class="n">match</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">negation</span><span class="p">)</span> <span class="ow">or</span> <span class="p">(</span><span class="n">negation</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">match</span><span class="p">)</span>
                        <span class="k">else</span> <span class="p">[]</span>
                    <span class="p">)</span>
                <span class="k">elif</span> <span class="n">operator</span> <span class="o">==</span> <span class="s2">&quot;!=&quot;</span><span class="p">:</span>
                    <span class="n">match</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">vals</span><span class="p">)</span> <span class="o">!=</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">conditional_value</span><span class="p">)</span>
                    <span class="n">vals</span> <span class="o">=</span> <span class="p">(</span>
                        <span class="p">[</span><span class="s2">&quot;True&quot;</span><span class="p">]</span>
                        <span class="k">if</span> <span class="p">(</span><span class="n">match</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">negation</span><span class="p">)</span> <span class="ow">or</span> <span class="p">(</span><span class="n">negation</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">match</span><span class="p">)</span>
                        <span class="k">else</span> <span class="p">[]</span>
                    <span class="p">)</span>
                <span class="k">elif</span> <span class="n">operator</span> <span class="o">==</span> <span class="s2">&quot;&lt;&quot;</span><span class="p">:</span>
                    <span class="n">vals</span> <span class="o">=</span> <span class="n">comparison_test</span><span class="p">(</span><span class="k">lambda</span> <span class="n">v</span><span class="p">,</span> <span class="n">c</span><span class="p">:</span> <span class="n">v</span> <span class="o">&lt;</span> <span class="n">c</span><span class="p">)</span>
                <span class="k">elif</span> <span class="n">operator</span> <span class="o">==</span> <span class="s2">&quot;&lt;=&quot;</span><span class="p">:</span>
                    <span class="n">vals</span> <span class="o">=</span> <span class="n">comparison_test</span><span class="p">(</span><span class="k">lambda</span> <span class="n">v</span><span class="p">,</span> <span class="n">c</span><span class="p">:</span> <span class="n">v</span> <span class="o">&lt;=</span> <span class="n">c</span><span class="p">)</span>
                <span class="k">elif</span> <span class="n">operator</span> <span class="o">==</span> <span class="s2">&quot;&gt;&quot;</span><span class="p">:</span>
                    <span class="n">vals</span> <span class="o">=</span> <span class="n">comparison_test</span><span class="p">(</span><span class="k">lambda</span> <span class="n">v</span><span class="p">,</span> <span class="n">c</span><span class="p">:</span> <span class="n">v</span> <span class="o">&gt;</span> <span class="n">c</span><span class="p">)</span>
                <span class="k">elif</span> <span class="n">operator</span> <span class="o">==</span> <span class="s2">&quot;&gt;=&quot;</span><span class="p">:</span>
                    <span class="n">vals</span> <span class="o">=</span> <span class="n">comparison_test</span><span class="p">(</span><span class="k">lambda</span> <span class="n">v</span><span class="p">,</span> <span class="n">c</span><span class="p">:</span> <span class="n">v</span> <span class="o">&gt;=</span> <span class="n">c</span><span class="p">)</span>

            <span class="k">if</span> <span class="n">is_combine</span> <span class="ow">and</span> <span class="n">combine_val</span><span class="p">:</span>
                <span class="n">vals</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">val</span> <span class="k">for</span> <span class="n">val</span> <span class="ow">in</span> <span class="n">combine_val</span> <span class="k">if</span> <span class="n">val</span><span class="p">)</span>

            <span class="k">if</span> <span class="n">is_bool</span><span class="p">:</span>
                <span class="n">vals</span> <span class="o">=</span> <span class="n">default</span> <span class="k">if</span> <span class="ow">not</span> <span class="n">vals</span> <span class="k">else</span> <span class="n">bool_val</span>
            <span class="k">elif</span> <span class="ow">not</span> <span class="n">vals</span> <span class="ow">and</span> <span class="n">field</span> <span class="o">!=</span> <span class="s2">&quot;var&quot;</span><span class="p">:</span>
                <span class="c1"># don&#39;t assign default value if the template was variable assignment</span>
                <span class="n">vals</span> <span class="o">=</span> <span class="n">default</span> <span class="ow">or</span> <span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">none_str</span><span class="p">]</span>

            <span class="n">pre</span> <span class="o">=</span> <span class="n">ts</span><span class="o">.</span><span class="n">pre</span> <span class="ow">or</span> <span class="s2">&quot;&quot;</span>
            <span class="n">post</span> <span class="o">=</span> <span class="n">ts</span><span class="o">.</span><span class="n">post</span> <span class="ow">or</span> <span class="s2">&quot;&quot;</span>

            <span class="n">rendered</span> <span class="o">=</span> <span class="p">[</span><span class="n">pre</span> <span class="o">+</span> <span class="nb">str</span><span class="p">(</span><span class="n">val</span><span class="p">)</span> <span class="o">+</span> <span class="n">post</span> <span class="k">for</span> <span class="n">val</span> <span class="ow">in</span> <span class="n">vals</span><span class="p">]</span> <span class="k">if</span> <span class="n">vals</span> <span class="k">else</span> <span class="p">[</span><span class="n">pre</span> <span class="o">+</span> <span class="n">post</span><span class="p">]</span>
            <span class="n">results_new</span> <span class="o">=</span> <span class="p">[]</span>
            <span class="k">for</span> <span class="n">ren</span> <span class="ow">in</span> <span class="n">rendered</span><span class="p">:</span>
                <span class="k">for</span> <span class="n">res</span> <span class="ow">in</span> <span class="n">results</span><span class="p">:</span>
                    <span class="n">res_new</span> <span class="o">=</span> <span class="n">res</span> <span class="o">+</span> <span class="n">ren</span>
                    <span class="n">results_new</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">res_new</span><span class="p">)</span>
            <span class="n">results</span> <span class="o">=</span> <span class="n">results_new</span>

        <span class="k">else</span><span class="p">:</span>
            <span class="c1"># no template</span>
            <span class="n">pre</span> <span class="o">=</span> <span class="n">ts</span><span class="o">.</span><span class="n">pre</span> <span class="ow">or</span> <span class="s2">&quot;&quot;</span>
            <span class="n">post</span> <span class="o">=</span> <span class="n">ts</span><span class="o">.</span><span class="n">post</span> <span class="ow">or</span> <span class="s2">&quot;&quot;</span>
            <span class="n">results</span> <span class="o">=</span> <span class="p">[</span><span class="n">r</span> <span class="o">+</span> <span class="n">pre</span> <span class="o">+</span> <span class="n">post</span> <span class="k">for</span> <span class="n">r</span> <span class="ow">in</span> <span class="n">results</span><span class="p">]</span>

        <span class="k">return</span> <span class="n">results</span><span class="p">,</span> <span class="n">unmatched</span>

<div class="viewcode-block" id="PhotoTemplate.expand_variables_to_str">
<a class="viewcode-back" href="../../reference.html#osxphotos.PhotoTemplate.expand_variables_to_str">[docs]</a>
    <span class="k">def</span><span class="w"> </span><span class="nf">expand_variables_to_str</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
<span class="w">        </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd">        Expand variables in value and return a str of the expanded value.</span>
<span class="sd">        Enforce that the expanded value is a single value, raises ValueError if not.</span>

<span class="sd">        Args:</span>
<span class="sd">            value: the value to expand</span>
<span class="sd">            name: the name of the value being expanded (used in error messages)</span>
<span class="sd">        &quot;&quot;&quot;</span>
        <span class="n">expanded</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">expand_variables</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
        <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">expanded</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">1</span><span class="p">:</span>
            <span class="k">raise</span> <span class="ne">SyntaxError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s2"> must have a single value, not </span><span class="si">{</span><span class="n">expanded</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">expanded</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span></div>


<div class="viewcode-block" id="PhotoTemplate.expand_variables">
<a class="viewcode-back" href="../../reference.html#osxphotos.PhotoTemplate.expand_variables">[docs]</a>
    <span class="k">def</span><span class="w"> </span><span class="nf">expand_variables</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">]:</span>
<span class="w">        </span><span class="sd">&quot;&quot;&quot;Expand variables in value&quot;&quot;&quot;</span>
        <span class="c1"># replace any variables with their values</span>
        <span class="n">values</span> <span class="o">=</span> <span class="p">[</span><span class="n">value</span><span class="p">]</span>
        <span class="n">new_values</span> <span class="o">=</span> <span class="p">[]</span>
        <span class="c1"># allow %% to escape %, match variables in form %var</span>
        <span class="n">variable_match</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">compile</span><span class="p">(</span><span class="sa">r</span><span class="s2">&quot;(?:</span><span class="si">%%</span><span class="s2">)*(%[\w]+)?&quot;</span><span class="p">)</span>
        <span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
            <span class="k">for</span> <span class="n">value</span> <span class="ow">in</span> <span class="n">values</span><span class="p">:</span>
                <span class="n">match</span> <span class="o">=</span> <span class="n">variable_match</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
                <span class="k">if</span> <span class="ow">not</span> <span class="n">match</span> <span class="ow">or</span> <span class="ow">not</span> <span class="n">match</span><span class="p">[</span><span class="mi">1</span><span class="p">]:</span>
                    <span class="k">break</span>
                <span class="n">var</span> <span class="o">=</span> <span class="n">match</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
                <span class="n">var_name</span> <span class="o">=</span> <span class="n">var</span><span class="p">[</span><span class="mi">1</span><span class="p">:]</span>
                <span class="k">if</span> <span class="n">var_name</span> <span class="ow">not</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">variables</span><span class="p">:</span>
                    <span class="k">raise</span> <span class="ne">SyntaxError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Variable &#39;</span><span class="si">{</span><span class="n">var_name</span><span class="si">}</span><span class="s2">&#39; is not defined.&quot;</span><span class="p">)</span>
                <span class="k">for</span> <span class="n">val</span> <span class="ow">in</span> <span class="n">values</span><span class="p">:</span>
                    <span class="k">for</span> <span class="n">var_val</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">variables</span><span class="p">[</span><span class="n">var_name</span><span class="p">]:</span>
                        <span class="n">new_values</span><span class="o">.</span><span class="n">append</span><span class="p">(</span>
                            <span class="n">re</span><span class="o">.</span><span class="n">sub</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;(%%)*</span><span class="si">{</span><span class="n">var</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">,</span> <span class="sa">r</span><span class="s2">&quot;\g&lt;1&gt;&quot;</span> <span class="o">+</span> <span class="n">var_val</span><span class="p">,</span> <span class="n">val</span><span class="p">)</span>
                        <span class="p">)</span>
            <span class="k">if</span> <span class="n">new_values</span> <span class="o">==</span> <span class="n">values</span> <span class="ow">or</span> <span class="ow">not</span> <span class="n">new_values</span><span class="p">:</span>
                <span class="k">break</span>
            <span class="n">values</span> <span class="o">=</span> <span class="n">new_values</span><span class="o">.</span><span class="n">copy</span><span class="p">()</span>
            <span class="n">new_values</span> <span class="o">=</span> <span class="p">[]</span>

        <span class="c1"># replace %% with %</span>
        <span class="c1"># any %% left in the string will be replaced with %</span>
        <span class="n">values</span> <span class="o">=</span> <span class="p">[</span><span class="n">value</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s2">&quot;</span><span class="si">%%</span><span class="s2">&quot;</span><span class="p">,</span> <span class="s2">&quot;%&quot;</span><span class="p">)</span> <span class="k">for</span> <span class="n">value</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>

        <span class="k">return</span> <span class="n">values</span></div>


<div class="viewcode-block" id="PhotoTemplate.get_field_values">
<a class="viewcode-back" href="../../reference.html#osxphotos.PhotoTemplate.get_field_values">[docs]</a>
    <span class="k">def</span><span class="w"> </span><span class="nf">get_field_values</span><span class="p">(</span>
        <span class="bp">self</span><span class="p">,</span>
        <span class="n">field</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
        <span class="n">subfield</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">str</span><span class="p">],</span>
        <span class="n">field_arg</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">str</span><span class="p">],</span>
        <span class="n">default</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">],</span>
    <span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Tuple</span><span class="p">[</span><span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">],</span> <span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">]]:</span>
<span class="w">        </span><span class="sd">&quot;&quot;&quot;Get the values for a field&quot;&quot;&quot;</span>
        <span class="n">vals</span> <span class="o">=</span> <span class="p">[]</span>
        <span class="n">unmatched</span> <span class="o">=</span> <span class="p">[]</span>
        <span class="k">if</span> <span class="p">(</span>
            <span class="n">field</span> <span class="ow">in</span> <span class="n">SINGLE_VALUE_SUBSTITUTIONS</span>
            <span class="ow">or</span> <span class="n">field</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot;.&quot;</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span> <span class="ow">in</span> <span class="n">SINGLE_VALUE_SUBSTITUTIONS</span>
        <span class="p">):</span>
            <span class="n">vals</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_template_value</span><span class="p">(</span>
                <span class="n">field</span><span class="p">,</span>
                <span class="n">default</span><span class="o">=</span><span class="n">default</span><span class="p">,</span>
                <span class="n">subfield</span><span class="o">=</span><span class="n">subfield</span><span class="p">,</span>
                <span class="n">field_arg</span><span class="o">=</span><span class="n">field_arg</span><span class="p">,</span>
            <span class="p">)</span>
        <span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">&quot;exiftool&quot;</span><span class="p">:</span>
            <span class="k">if</span> <span class="n">subfield</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
                <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span>
                    <span class="s2">&quot;SyntaxError: GROUP:NAME subfield must not be null with {exiftool:GROUP:NAME}&#39;&quot;</span>
                <span class="p">)</span>
            <span class="n">vals</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_template_value_exiftool</span><span class="p">(</span>
                <span class="n">subfield</span><span class="p">,</span>
            <span class="p">)</span>
        <span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">&quot;function&quot;</span><span class="p">:</span>
            <span class="k">if</span> <span class="n">subfield</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
                <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span>
                    <span class="s2">&quot;SyntaxError: filename and function must not be null with {function:filename.py::function_name}&quot;</span>
                <span class="p">)</span>
            <span class="n">vals</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_template_value_function</span><span class="p">(</span>
                <span class="n">subfield</span><span class="p">,</span> <span class="n">field_arg</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">caller</span>
            <span class="p">)</span>
        <span class="k">elif</span> <span class="n">field</span> <span class="ow">in</span> <span class="n">MULTI_VALUE_SUBSTITUTIONS</span> <span class="ow">or</span> <span class="n">field</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s2">&quot;photo&quot;</span><span class="p">):</span>
            <span class="n">vals</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_template_value_multi</span><span class="p">(</span>
                <span class="n">field</span><span class="p">,</span> <span class="n">subfield</span><span class="p">,</span> <span class="n">path_sep</span><span class="o">=</span><span class="n">field_arg</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="n">default</span>
            <span class="p">)</span>
        <span class="k">elif</span> <span class="n">field</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot;.&quot;</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span> <span class="ow">in</span> <span class="n">PATHLIB_SUBSTITUTIONS</span><span class="p">:</span>
            <span class="n">vals</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_template_value_pathlib</span><span class="p">(</span><span class="n">field</span><span class="p">)</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">unmatched</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">field</span><span class="p">)</span>
            <span class="k">return</span> <span class="p">[],</span> <span class="n">unmatched</span>
        <span class="k">return</span> <span class="n">vals</span><span class="p">,</span> <span class="n">unmatched</span></div>


<div class="viewcode-block" id="PhotoTemplate.get_template_value">
<a class="viewcode-back" href="../../reference.html#osxphotos.PhotoTemplate.get_template_value">[docs]</a>
    <span class="k">def</span><span class="w"> </span><span class="nf">get_template_value</span><span class="p">(</span>
        <span class="bp">self</span><span class="p">,</span>
        <span class="n">field</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
        <span class="n">default</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">],</span>
        <span class="n">subfield</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">str</span><span class="p">],</span>
        <span class="n">field_arg</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">str</span><span class="p">],</span>
    <span class="p">):</span>
<span class="w">        </span><span class="sd">&quot;&quot;&quot;lookup value for template field (single-value template substitutions)</span>

<span class="sd">        Args:</span>
<span class="sd">            field: template field to find value for.</span>
<span class="sd">            default: the default value provided by the user</span>
<span class="sd">            bool_val: True value if expression is boolean</span>
<span class="sd">            delim: delimiter for expand in place</span>
<span class="sd">            path_sep: path separator for fields that are path-like</span>
<span class="sd">            subfield: subfield (value after : in field)</span>

<span class="sd">        Returns:</span>
<span class="sd">            The matching template value (which may be None).</span>

<span class="sd">        Raises:</span>
<span class="sd">            ValueError if no rule exists for field.</span>
<span class="sd">        &quot;&quot;&quot;</span>

        <span class="c1"># initialize today with current date/time if needed</span>
        <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">today</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
            <span class="bp">self</span><span class="o">.</span><span class="n">today</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span>

        <span class="n">value</span> <span class="o">=</span> <span class="kc">None</span>

        <span class="c1"># wouldn&#39;t a switch/case statement be nice...</span>
        <span class="c1"># handle the fields that don&#39;t require a PhotoInfo object first</span>
        <span class="k">if</span> <span class="n">field</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s2">&quot;today&quot;</span><span class="p">):</span>
            <span class="n">value</span> <span class="o">=</span> <span class="n">format_date_field</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">today</span><span class="p">,</span> <span class="n">field</span><span class="p">,</span> <span class="n">default</span><span class="p">)</span>
        <span class="k">elif</span> <span class="n">field</span> <span class="ow">in</span> <span class="n">PUNCTUATION</span><span class="p">:</span>
            <span class="n">value</span> <span class="o">=</span> <span class="n">PUNCTUATION</span><span class="p">[</span><span class="n">field</span><span class="p">]</span>
        <span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">&quot;osxphotos_version&quot;</span><span class="p">:</span>
            <span class="n">value</span> <span class="o">=</span> <span class="n">__version__</span>
        <span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">&quot;osxphotos_cmd_line&quot;</span><span class="p">:</span>
            <span class="n">value</span> <span class="o">=</span> <span class="s2">&quot; &quot;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">)</span>
        <span class="k">elif</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">uuid</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
            <span class="c1"># if no uuid, don&#39;t have a PhotoInfo object (could be PhotoInfoNone)</span>
            <span class="c1"># so don&#39;t try to handle any of the photo fields</span>
            <span class="k">return</span> <span class="p">[]</span>
        <span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">&quot;name&quot;</span><span class="p">:</span>
            <span class="n">value</span> <span class="o">=</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">filename</span><span class="p">)</span><span class="o">.</span><span class="n">stem</span>
        <span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">&quot;original_name&quot;</span><span class="p">:</span>
            <span class="n">value</span> <span class="o">=</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">original_filename</span><span class="p">)</span><span class="o">.</span><span class="n">stem</span>
        <span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">&quot;title&quot;</span><span class="p">:</span>
            <span class="n">value</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">title</span>
        <span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">&quot;descr&quot;</span><span class="p">:</span>
            <span class="n">value</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">description</span>
        <span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">&quot;media_type&quot;</span><span class="p">:</span>
            <span class="n">value</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_media_type</span><span class="p">(</span><span class="n">default</span><span class="p">)</span>
        <span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">&quot;photo_or_video&quot;</span><span class="p">:</span>
            <span class="n">value</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_photo_video_type</span><span class="p">(</span><span class="n">default</span><span class="p">)</span>
        <span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">&quot;hdr&quot;</span><span class="p">:</span>
            <span class="n">value</span> <span class="o">=</span> <span class="s2">&quot;hdr&quot;</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">hdr</span> <span class="k">else</span> <span class="kc">None</span>
        <span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">&quot;edited&quot;</span><span class="p">:</span>
            <span class="n">value</span> <span class="o">=</span> <span class="s2">&quot;edited&quot;</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">hasadjustments</span> <span class="k">else</span> <span class="kc">None</span>
        <span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">&quot;edited_version&quot;</span><span class="p">:</span>
            <span class="n">value</span> <span class="o">=</span> <span class="s2">&quot;edited_version&quot;</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">edited_version</span> <span class="k">else</span> <span class="kc">None</span>
        <span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">&quot;favorite&quot;</span><span class="p">:</span>
            <span class="n">value</span> <span class="o">=</span> <span class="s2">&quot;favorite&quot;</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">favorite</span> <span class="k">else</span> <span class="kc">None</span>
        <span class="k">elif</span> <span class="n">field</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s2">&quot;created&quot;</span><span class="p">):</span>
            <span class="n">value</span> <span class="o">=</span> <span class="n">format_date_field</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">date</span><span class="p">,</span> <span class="n">field</span><span class="p">,</span> <span class="n">default</span><span class="p">)</span>
        <span class="k">elif</span> <span class="n">field</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s2">&quot;modified&quot;</span><span class="p">):</span>
            <span class="c1"># if no modified date, use photo.date</span>
            <span class="n">value</span> <span class="o">=</span> <span class="n">format_date_field</span><span class="p">(</span>
                <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">date_modified</span> <span class="ow">or</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">date</span><span class="p">,</span> <span class="n">field</span><span class="p">,</span> <span class="n">default</span>
            <span class="p">)</span>
        <span class="k">elif</span> <span class="n">field</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s2">&quot;place&quot;</span><span class="p">):</span>
            <span class="n">value</span> <span class="o">=</span> <span class="n">get_place_value</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="p">,</span> <span class="n">field</span><span class="p">)</span>
        <span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">&quot;searchinfo.season&quot;</span><span class="p">:</span>
            <span class="n">value</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">search_info</span><span class="o">.</span><span class="n">season</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">search_info</span> <span class="k">else</span> <span class="kc">None</span>
        <span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">&quot;exif.camera_make&quot;</span><span class="p">:</span>
            <span class="n">value</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">exif_info</span><span class="o">.</span><span class="n">camera_make</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">exif_info</span> <span class="k">else</span> <span class="kc">None</span>
        <span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">&quot;exif.camera_model&quot;</span><span class="p">:</span>
            <span class="n">value</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">exif_info</span><span class="o">.</span><span class="n">camera_model</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">exif_info</span> <span class="k">else</span> <span class="kc">None</span>
        <span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">&quot;exif.lens_model&quot;</span><span class="p">:</span>
            <span class="n">value</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">exif_info</span><span class="o">.</span><span class="n">lens_model</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">exif_info</span> <span class="k">else</span> <span class="kc">None</span>
        <span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">&quot;moment&quot;</span><span class="p">:</span>
            <span class="n">value</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">moment_info</span><span class="o">.</span><span class="n">title</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">moment_info</span> <span class="k">else</span> <span class="kc">None</span>
        <span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">&quot;uuid&quot;</span><span class="p">:</span>
            <span class="n">value</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">uuid</span>
        <span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">&quot;shortuuid&quot;</span><span class="p">:</span>
            <span class="n">value</span> <span class="o">=</span> <span class="n">uuid_to_shortuuid</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">uuid</span><span class="p">)</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">uuid</span> <span class="k">else</span> <span class="kc">None</span>
        <span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">&quot;id&quot;</span><span class="p">:</span>
            <span class="n">value</span> <span class="o">=</span> <span class="n">format_str_value</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;pk&quot;</span><span class="p">],</span> <span class="n">subfield</span><span class="p">)</span>
        <span class="k">elif</span> <span class="n">field</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s2">&quot;album_seq&quot;</span><span class="p">)</span> <span class="ow">or</span> <span class="n">field</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s2">&quot;folder_album_seq&quot;</span><span class="p">):</span>
            <span class="k">if</span> <span class="n">dest_path</span> <span class="o">:=</span> <span class="bp">self</span><span class="o">.</span><span class="n">dest_path</span><span class="p">:</span>
                <span class="k">if</span> <span class="n">field</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s2">&quot;album_seq&quot;</span><span class="p">):</span>
                    <span class="n">album</span> <span class="o">=</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="n">dest_path</span><span class="p">)</span><span class="o">.</span><span class="n">name</span>
                    <span class="n">album_info</span> <span class="o">=</span> <span class="n">_get_album_by_name</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="p">,</span> <span class="n">album</span><span class="p">)</span>
                <span class="k">else</span><span class="p">:</span>
                    <span class="n">album_info</span> <span class="o">=</span> <span class="n">_get_album_by_path</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="p">,</span> <span class="n">dest_path</span><span class="p">)</span>
                <span class="n">value</span> <span class="o">=</span> <span class="n">album_info</span><span class="o">.</span><span class="n">photo_index</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="p">)</span> <span class="k">if</span> <span class="n">album_info</span> <span class="k">else</span> <span class="kc">None</span>
            <span class="k">else</span><span class="p">:</span>
                <span class="n">value</span> <span class="o">=</span> <span class="kc">None</span>
            <span class="k">if</span> <span class="n">value</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
                <span class="n">start_id</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">field_arg</span><span class="p">)</span> <span class="k">if</span> <span class="n">field_arg</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="k">else</span> <span class="mi">0</span>
                <span class="n">value</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">value</span><span class="p">)</span> <span class="o">+</span> <span class="n">start_id</span>
                <span class="n">value</span> <span class="o">=</span> <span class="n">format_str_value</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="n">subfield</span><span class="p">)</span>
        <span class="k">elif</span> <span class="n">field</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s2">&quot;counter&quot;</span><span class="p">):</span>
            <span class="n">value</span> <span class="o">=</span> <span class="n">counter</span><span class="o">.</span><span class="n">get_counter_value</span><span class="p">(</span><span class="n">field</span><span class="p">,</span> <span class="n">subfield</span><span class="p">,</span> <span class="n">field_arg</span><span class="p">)</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="c1"># if here, didn&#39;t get a match</span>
            <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Unhandled template value: </span><span class="si">{</span><span class="n">field</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>

        <span class="c1"># sanitize filename or directory name if needed</span>
        <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">filename</span><span class="p">:</span>
            <span class="n">value</span> <span class="o">=</span> <span class="n">sanitize_pathpart</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
        <span class="k">elif</span> <span class="bp">self</span><span class="o">.</span><span class="n">dirname</span><span class="p">:</span>
            <span class="n">value</span> <span class="o">=</span> <span class="n">sanitize_dirname</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>

        <span class="c1"># ensure no empty strings in value (see #512)</span>
        <span class="n">value</span> <span class="o">=</span> <span class="kc">None</span> <span class="k">if</span> <span class="n">value</span> <span class="o">==</span> <span class="s2">&quot;&quot;</span> <span class="k">else</span> <span class="n">value</span>

        <span class="k">return</span> <span class="p">[</span><span class="n">value</span><span class="p">]</span></div>


<div class="viewcode-block" id="PhotoTemplate.get_template_value_pathlib">
<a class="viewcode-back" href="../../reference.html#osxphotos.PhotoTemplate.get_template_value_pathlib">[docs]</a>
    <span class="k">def</span><span class="w"> </span><span class="nf">get_template_value_pathlib</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">field</span><span class="p">):</span>
<span class="w">        </span><span class="sd">&quot;&quot;&quot;lookup value for template pathlib template fields</span>

<span class="sd">        Args:</span>
<span class="sd">            field: template field to find value for.</span>

<span class="sd">        Returns:</span>
<span class="sd">            The matching template value (which may be None).</span>

<span class="sd">        Raises:</span>
<span class="sd">            ValueError if no rule exists for field.</span>
<span class="sd">        &quot;&quot;&quot;</span>
        <span class="n">field_stem</span> <span class="o">=</span> <span class="n">field</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot;.&quot;</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
        <span class="k">if</span> <span class="n">field_stem</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">PATHLIB_SUBSTITUTIONS</span><span class="p">:</span>
            <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;SyntaxError: Unknown field: </span><span class="si">{</span><span class="n">field</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>

        <span class="n">field_value</span> <span class="o">=</span> <span class="kc">None</span>
        <span class="k">try</span><span class="p">:</span>
            <span class="n">field_value</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">field_stem</span><span class="p">)</span>
        <span class="k">except</span> <span class="ne">AttributeError</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
            <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Unknown path-like field: </span><span class="si">{</span><span class="n">field_stem</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span> <span class="kn">from</span><span class="w"> </span><span class="nn">e</span>
        <span class="n">value</span> <span class="o">=</span> <span class="n">_get_pathlib_value</span><span class="p">(</span><span class="n">field</span><span class="p">,</span> <span class="n">field_value</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">quote</span><span class="p">)</span>

        <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">filename</span><span class="p">:</span>
            <span class="n">value</span> <span class="o">=</span> <span class="n">sanitize_pathpart</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
        <span class="k">elif</span> <span class="bp">self</span><span class="o">.</span><span class="n">dirname</span><span class="p">:</span>
            <span class="n">value</span> <span class="o">=</span> <span class="n">sanitize_dirname</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>

        <span class="k">return</span> <span class="p">[</span><span class="n">value</span><span class="p">]</span></div>


<div class="viewcode-block" id="PhotoTemplate.get_filter_values">
<a class="viewcode-back" href="../../reference.html#osxphotos.PhotoTemplate.get_filter_values">[docs]</a>
    <span class="k">def</span><span class="w"> </span><span class="nf">get_filter_values</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">filter_</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">values</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">]:</span>
<span class="w">        </span><span class="sd">&quot;&quot;&quot;Return filtered values&quot;&quot;&quot;</span>

        <span class="c1"># extract args, if any</span>
        <span class="k">if</span> <span class="n">re</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="sa">r</span><span class="s2">&quot;\(.*\)&quot;</span><span class="p">,</span> <span class="n">filter_</span><span class="p">):</span>
            <span class="n">filter_</span><span class="p">,</span> <span class="n">args</span> <span class="o">=</span> <span class="n">filter_</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot;(&quot;</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
            <span class="n">args</span> <span class="o">=</span> <span class="n">args</span><span class="o">.</span><span class="n">rstrip</span><span class="p">(</span><span class="s2">&quot;)&quot;</span><span class="p">)</span>
            <span class="n">args</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">expand_variables_to_str</span><span class="p">(</span><span class="n">args</span><span class="p">,</span> <span class="s2">&quot;Filter arguments&quot;</span><span class="p">)</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">args</span> <span class="o">=</span> <span class="kc">None</span>

        <span class="c1"># check that filter name (without subfields or arguments) is valid</span>
        <span class="n">valid_filters</span> <span class="o">=</span> <span class="p">[</span><span class="n">f</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot;(&quot;</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span> <span class="k">for</span> <span class="n">f</span> <span class="ow">in</span> <span class="n">FILTER_VALUES</span><span class="p">]</span>
        <span class="k">if</span> <span class="n">filter_</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot;:&quot;</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">valid_filters</span><span class="p">:</span>
            <span class="k">raise</span> <span class="ne">SyntaxError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Unknown filter: </span><span class="si">{</span><span class="n">filter_</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>

        <span class="k">if</span> <span class="n">filter_</span> <span class="ow">in</span> <span class="p">[</span>
            <span class="s2">&quot;append&quot;</span><span class="p">,</span>
            <span class="s2">&quot;appends&quot;</span><span class="p">,</span>
            <span class="s2">&quot;chomp&quot;</span><span class="p">,</span>
            <span class="s2">&quot;chop&quot;</span><span class="p">,</span>
            <span class="s2">&quot;filter&quot;</span><span class="p">,</span>
            <span class="s2">&quot;prepend&quot;</span><span class="p">,</span>
            <span class="s2">&quot;prepends&quot;</span><span class="p">,</span>
            <span class="s2">&quot;remove&quot;</span><span class="p">,</span>
            <span class="s2">&quot;slice&quot;</span><span class="p">,</span>
            <span class="s2">&quot;split&quot;</span><span class="p">,</span>
            <span class="s2">&quot;sslice&quot;</span><span class="p">,</span>
        <span class="p">]</span> <span class="ow">and</span> <span class="p">(</span><span class="n">args</span> <span class="ow">is</span> <span class="kc">None</span> <span class="ow">or</span> <span class="ow">not</span> <span class="nb">len</span><span class="p">(</span><span class="n">args</span><span class="p">)):</span>
            <span class="k">raise</span> <span class="ne">SyntaxError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">filter_</span><span class="si">}</span><span class="s2"> requires arguments&quot;</span><span class="p">)</span>

        <span class="k">if</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">&quot;lower&quot;</span><span class="p">:</span>
            <span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="n">v</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
        <span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">&quot;upper&quot;</span><span class="p">:</span>
            <span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="n">v</span><span class="o">.</span><span class="n">upper</span><span class="p">()</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
        <span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">&quot;strip&quot;</span><span class="p">:</span>
            <span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="n">v</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
        <span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">&quot;capitalize&quot;</span><span class="p">:</span>
            <span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="n">v</span><span class="o">.</span><span class="n">capitalize</span><span class="p">()</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
        <span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">&quot;titlecase&quot;</span><span class="p">:</span>
            <span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="n">v</span><span class="o">.</span><span class="n">title</span><span class="p">()</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
        <span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">&quot;braces&quot;</span><span class="p">:</span>
            <span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;{&quot;</span> <span class="o">+</span> <span class="n">v</span> <span class="o">+</span> <span class="s2">&quot;}&quot;</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
        <span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">&quot;parens&quot;</span><span class="p">:</span>
            <span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="sa">f</span><span class="s2">&quot;(</span><span class="si">{</span><span class="n">v</span><span class="si">}</span><span class="s2">)&quot;</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
        <span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">&quot;brackets&quot;</span><span class="p">:</span>
            <span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="sa">f</span><span class="s2">&quot;[</span><span class="si">{</span><span class="n">v</span><span class="si">}</span><span class="s2">]&quot;</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
        <span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">&quot;shell_quote&quot;</span><span class="p">:</span>
            <span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="n">shlex</span><span class="o">.</span><span class="n">quote</span><span class="p">(</span><span class="n">v</span><span class="p">)</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
        <span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">&quot;split&quot;</span><span class="p">:</span>
            <span class="k">if</span> <span class="n">delim</span> <span class="o">:=</span> <span class="n">args</span><span class="p">:</span>
                <span class="n">new_values</span> <span class="o">=</span> <span class="p">[]</span>
                <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">:</span>
                    <span class="n">new_values</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">v</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="n">delim</span><span class="p">))</span>
                <span class="n">value</span> <span class="o">=</span> <span class="n">new_values</span>
            <span class="k">else</span><span class="p">:</span>
                <span class="n">value</span> <span class="o">=</span> <span class="n">values</span>
        <span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">&quot;chop&quot;</span><span class="p">:</span>
            <span class="c1"># chop off characters from the end</span>
            <span class="k">try</span><span class="p">:</span>
                <span class="n">chop</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">args</span><span class="p">)</span>
            <span class="k">except</span> <span class="ne">ValueError</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
                <span class="k">raise</span> <span class="ne">SyntaxError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Invalid value for chop: </span><span class="si">{</span><span class="n">args</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span> <span class="kn">from</span><span class="w"> </span><span class="nn">e</span>
            <span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="n">v</span><span class="p">[:</span><span class="o">-</span><span class="n">chop</span><span class="p">]</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span> <span class="k">if</span> <span class="n">chop</span> <span class="k">else</span> <span class="n">values</span>
        <span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">&quot;chomp&quot;</span><span class="p">:</span>
            <span class="c1"># chop off characters from the beginning</span>
            <span class="k">try</span><span class="p">:</span>
                <span class="n">chomp</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">args</span><span class="p">)</span>
            <span class="k">except</span> <span class="ne">ValueError</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
                <span class="k">raise</span> <span class="ne">SyntaxError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Invalid value for chomp: </span><span class="si">{</span><span class="n">args</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span> <span class="kn">from</span><span class="w"> </span><span class="nn">e</span>
            <span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="n">v</span><span class="p">[</span><span class="n">chomp</span><span class="p">:]</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span> <span class="k">if</span> <span class="n">chomp</span> <span class="k">else</span> <span class="n">values</span>
        <span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">&quot;autosplit&quot;</span><span class="p">:</span>
            <span class="c1"># try to split keyword strings automatically</span>
            <span class="n">temp_values</span> <span class="o">=</span> <span class="p">[</span><span class="n">v</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s2">&quot;,&quot;</span><span class="p">,</span> <span class="s2">&quot; &quot;</span><span class="p">)</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
            <span class="n">temp_values</span> <span class="o">=</span> <span class="p">[</span><span class="n">v</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s2">&quot;;&quot;</span><span class="p">,</span> <span class="s2">&quot; &quot;</span><span class="p">)</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">temp_values</span><span class="p">]</span>
            <span class="n">value</span> <span class="o">=</span> <span class="p">[]</span>
            <span class="k">for</span> <span class="n">val</span> <span class="ow">in</span> <span class="n">temp_values</span><span class="p">:</span>
                <span class="n">value</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">val</span><span class="o">.</span><span class="n">split</span><span class="p">())</span>
        <span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">&quot;sort&quot;</span><span class="p">:</span>
            <span class="c1"># sort list of values</span>
            <span class="n">value</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">values</span><span class="p">)</span>
        <span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">&quot;rsort&quot;</span><span class="p">:</span>
            <span class="c1"># reverse sort list of values</span>
            <span class="n">value</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">values</span><span class="p">,</span> <span class="n">reverse</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
        <span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">&quot;reverse&quot;</span><span class="p">:</span>
            <span class="c1"># reverse list of values</span>
            <span class="n">value</span> <span class="o">=</span> <span class="n">values</span><span class="p">[::</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
        <span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">&quot;uniq&quot;</span><span class="p">:</span>
            <span class="c1"># remove duplicate values from list</span>
            <span class="n">temp_values</span> <span class="o">=</span> <span class="p">[]</span>
            <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">:</span>
                <span class="k">if</span> <span class="n">v</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">temp_values</span><span class="p">:</span>
                    <span class="n">temp_values</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">v</span><span class="p">)</span>
            <span class="n">value</span> <span class="o">=</span> <span class="n">temp_values</span>
        <span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">&quot;join&quot;</span><span class="p">:</span>
            <span class="c1"># join list of values with delimiter</span>
            <span class="n">delim</span> <span class="o">=</span> <span class="n">args</span> <span class="ow">or</span> <span class="s2">&quot;&quot;</span>
            <span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="n">delim</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">values</span><span class="p">)]</span>
        <span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">&quot;append&quot;</span><span class="p">:</span>
            <span class="c1"># append value to list</span>
            <span class="n">value</span> <span class="o">=</span> <span class="n">values</span> <span class="o">+</span> <span class="p">[</span><span class="n">args</span><span class="p">]</span>
        <span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">&quot;prepend&quot;</span><span class="p">:</span>
            <span class="c1"># prepend value to list</span>
            <span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="n">args</span><span class="p">]</span> <span class="o">+</span> <span class="n">values</span>
        <span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">&quot;appends&quot;</span><span class="p">:</span>
            <span class="c1"># append value to each item in list</span>
            <span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">v</span><span class="si">}{</span><span class="n">args</span><span class="si">}</span><span class="s2">&quot;</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
        <span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">&quot;prepends&quot;</span><span class="p">:</span>
            <span class="c1"># prepend value to each item in list</span>
            <span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">args</span><span class="si">}{</span><span class="n">v</span><span class="si">}</span><span class="s2">&quot;</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
        <span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">&quot;remove&quot;</span><span class="p">:</span>
            <span class="c1"># remove value from list</span>
            <span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="n">v</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span> <span class="k">if</span> <span class="n">v</span> <span class="o">!=</span> <span class="n">args</span><span class="p">]</span>
        <span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">&quot;slice&quot;</span><span class="p">:</span>
            <span class="c1"># slice list of values</span>
            <span class="n">value</span> <span class="o">=</span> <span class="n">values</span><span class="p">[</span><span class="n">create_slice</span><span class="p">(</span><span class="n">args</span><span class="p">)]</span>
        <span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">&quot;sslice&quot;</span><span class="p">:</span>
            <span class="c1"># slice each value in a list</span>
            <span class="n">slice_</span> <span class="o">=</span> <span class="n">create_slice</span><span class="p">(</span><span class="n">args</span><span class="p">)</span>
            <span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="n">v</span><span class="p">[</span><span class="n">slice_</span><span class="p">]</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
        <span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">&quot;filter&quot;</span><span class="p">:</span>
            <span class="c1"># filter values based on a predicate</span>
            <span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="n">v</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">filter_predicate</span><span class="p">(</span><span class="n">v</span><span class="p">,</span> <span class="n">args</span><span class="p">)]</span>
        <span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">&quot;int&quot;</span><span class="p">:</span>
            <span class="c1"># convert value to integer</span>
            <span class="n">value</span> <span class="o">=</span> <span class="n">values_to_int</span><span class="p">(</span><span class="n">values</span><span class="p">)</span>
        <span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">&quot;float&quot;</span><span class="p">:</span>
            <span class="c1"># convert value to float</span>
            <span class="n">value</span> <span class="o">=</span> <span class="n">values_to_float</span><span class="p">(</span><span class="n">values</span><span class="p">)</span>
        <span class="k">elif</span> <span class="n">filter_</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s2">&quot;function:&quot;</span><span class="p">):</span>
            <span class="n">value</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_template_value_filter_function</span><span class="p">(</span><span class="n">filter_</span><span class="p">,</span> <span class="n">args</span><span class="p">,</span> <span class="n">values</span><span class="p">)</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">value</span> <span class="o">=</span> <span class="p">[]</span>
        <span class="k">return</span> <span class="n">value</span></div>


<div class="viewcode-block" id="PhotoTemplate.filter_predicate">
<a class="viewcode-back" href="../../reference.html#osxphotos.PhotoTemplate.filter_predicate">[docs]</a>
    <span class="k">def</span><span class="w"> </span><span class="nf">filter_predicate</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">args</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">bool</span><span class="p">:</span>
<span class="w">        </span><span class="sd">&quot;&quot;&quot;Return True if value passes predicate&quot;&quot;&quot;</span>

        <span class="c1"># extract function name and arguments</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="n">args</span><span class="p">:</span>
            <span class="k">raise</span> <span class="ne">SyntaxError</span><span class="p">(</span><span class="s2">&quot;Filter predicate requires arguments&quot;</span><span class="p">)</span>
        <span class="n">args</span> <span class="o">=</span> <span class="n">args</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="kc">None</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">args</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="s2">&quot;not&quot;</span><span class="p">:</span>
            <span class="n">args</span> <span class="o">=</span> <span class="n">args</span><span class="p">[</span><span class="mi">1</span><span class="p">:]</span>
            <span class="k">return</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">filter_predicate</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="s2">&quot; &quot;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">args</span><span class="p">))</span>

        <span class="n">predicate</span> <span class="o">=</span> <span class="n">args</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
        <span class="n">conditional_value</span> <span class="o">=</span> <span class="n">args</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot;|&quot;</span><span class="p">)</span>

        <span class="k">def</span><span class="w"> </span><span class="nf">comparison_test</span><span class="p">(</span><span class="n">test_function</span><span class="p">):</span>
<span class="w">            </span><span class="sd">&quot;&quot;&quot;Perform numerical comparisons using test_function&quot;&quot;&quot;</span>
            <span class="c1"># returns True if any of the values match the condition</span>
            <span class="k">try</span><span class="p">:</span>
                <span class="k">return</span> <span class="nb">any</span><span class="p">(</span>
                    <span class="nb">bool</span><span class="p">(</span><span class="n">test_function</span><span class="p">(</span><span class="nb">float</span><span class="p">(</span><span class="n">value</span><span class="p">),</span> <span class="nb">float</span><span class="p">(</span><span class="n">c</span><span class="p">)))</span>
                    <span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="n">conditional_value</span>
                <span class="p">)</span>
            <span class="k">except</span> <span class="ne">ValueError</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
                <span class="k">raise</span> <span class="ne">SyntaxError</span><span class="p">(</span>
                    <span class="sa">f</span><span class="s2">&quot;comparison operators may only be used with values that can be converted to numbers: </span><span class="si">{</span><span class="n">value</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">conditional_value</span><span class="si">}</span><span class="s2">&quot;</span>
                <span class="p">)</span> <span class="kn">from</span><span class="w"> </span><span class="nn">e</span>

        <span class="n">predicate_is_true</span> <span class="o">=</span> <span class="kc">False</span>
        <span class="k">if</span> <span class="n">predicate</span> <span class="o">==</span> <span class="s2">&quot;contains&quot;</span><span class="p">:</span>
            <span class="n">predicate_is_true</span> <span class="o">=</span> <span class="nb">any</span><span class="p">(</span><span class="n">c</span> <span class="ow">in</span> <span class="n">value</span> <span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="n">conditional_value</span><span class="p">)</span>
        <span class="k">elif</span> <span class="n">predicate</span> <span class="o">==</span> <span class="s2">&quot;endswith&quot;</span><span class="p">:</span>
            <span class="n">predicate_is_true</span> <span class="o">=</span> <span class="nb">any</span><span class="p">(</span><span class="n">value</span><span class="o">.</span><span class="n">endswith</span><span class="p">(</span><span class="n">c</span><span class="p">)</span> <span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="n">conditional_value</span><span class="p">)</span>
        <span class="k">elif</span> <span class="n">predicate</span> <span class="ow">in</span> <span class="p">[</span><span class="s2">&quot;matches&quot;</span><span class="p">,</span> <span class="s2">&quot;==&quot;</span><span class="p">]:</span>
            <span class="n">predicate_is_true</span> <span class="o">=</span> <span class="nb">any</span><span class="p">(</span><span class="n">value</span> <span class="o">==</span> <span class="n">c</span> <span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="n">conditional_value</span><span class="p">)</span>
        <span class="k">elif</span> <span class="n">predicate</span> <span class="o">==</span> <span class="s2">&quot;startswith&quot;</span><span class="p">:</span>
            <span class="n">predicate_is_true</span> <span class="o">=</span> <span class="nb">any</span><span class="p">(</span><span class="n">value</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="n">c</span><span class="p">)</span> <span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="n">conditional_value</span><span class="p">)</span>
        <span class="k">elif</span> <span class="n">predicate</span> <span class="o">==</span> <span class="s2">&quot;!=&quot;</span><span class="p">:</span>
            <span class="n">predicate_is_true</span> <span class="o">=</span> <span class="nb">any</span><span class="p">(</span><span class="n">value</span> <span class="o">!=</span> <span class="n">c</span> <span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="n">conditional_value</span><span class="p">)</span>
        <span class="k">elif</span> <span class="n">predicate</span> <span class="o">==</span> <span class="s2">&quot;&lt;&quot;</span><span class="p">:</span>
            <span class="n">predicate_is_true</span> <span class="o">=</span> <span class="n">comparison_test</span><span class="p">(</span><span class="k">lambda</span> <span class="n">v</span><span class="p">,</span> <span class="n">c</span><span class="p">:</span> <span class="n">v</span> <span class="o">&lt;</span> <span class="n">c</span><span class="p">)</span>
        <span class="k">elif</span> <span class="n">predicate</span> <span class="o">==</span> <span class="s2">&quot;&lt;=&quot;</span><span class="p">:</span>
            <span class="n">predicate_is_true</span> <span class="o">=</span> <span class="n">comparison_test</span><span class="p">(</span><span class="k">lambda</span> <span class="n">v</span><span class="p">,</span> <span class="n">c</span><span class="p">:</span> <span class="n">v</span> <span class="o">&lt;=</span> <span class="n">c</span><span class="p">)</span>
        <span class="k">elif</span> <span class="n">predicate</span> <span class="o">==</span> <span class="s2">&quot;&gt;&quot;</span><span class="p">:</span>
            <span class="n">predicate_is_true</span> <span class="o">=</span> <span class="n">comparison_test</span><span class="p">(</span><span class="k">lambda</span> <span class="n">v</span><span class="p">,</span> <span class="n">c</span><span class="p">:</span> <span class="n">v</span> <span class="o">&gt;</span> <span class="n">c</span><span class="p">)</span>
        <span class="k">elif</span> <span class="n">predicate</span> <span class="o">==</span> <span class="s2">&quot;&gt;=&quot;</span><span class="p">:</span>
            <span class="n">predicate_is_true</span> <span class="o">=</span> <span class="n">comparison_test</span><span class="p">(</span><span class="k">lambda</span> <span class="n">v</span><span class="p">,</span> <span class="n">c</span><span class="p">:</span> <span class="n">v</span> <span class="o">&gt;=</span> <span class="n">c</span><span class="p">)</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="k">raise</span> <span class="ne">SyntaxError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Invalid predicate: </span><span class="si">{</span><span class="n">predicate</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>

        <span class="k">return</span> <span class="n">predicate_is_true</span></div>


<div class="viewcode-block" id="PhotoTemplate.get_template_value_multi">
<a class="viewcode-back" href="../../reference.html#osxphotos.PhotoTemplate.get_template_value_multi">[docs]</a>
    <span class="k">def</span><span class="w"> </span><span class="nf">get_template_value_multi</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">field</span><span class="p">,</span> <span class="n">subfield</span><span class="p">,</span> <span class="n">path_sep</span><span class="p">,</span> <span class="n">default</span><span class="p">):</span>
<span class="w">        </span><span class="sd">&quot;&quot;&quot;lookup value for template field (multi-value template substitutions)</span>

<span class="sd">        Args:</span>
<span class="sd">            field: template field to find value for.</span>
<span class="sd">            subfield: the template subfield value</span>
<span class="sd">            path_sep: path separator to use for folder_album field</span>
<span class="sd">            default: value of default field</span>

<span class="sd">        Returns:</span>
<span class="sd">            List of the matching template values or [].</span>

<span class="sd">        Raises:</span>
<span class="sd">            ValueError if no rule exists for field.</span>
<span class="sd">        &quot;&quot;&quot;</span>

<span class="w">        </span><span class="sd">&quot;&quot;&quot; return list of values for a multi-valued template field &quot;&quot;&quot;</span>

        <span class="n">path_sep</span> <span class="o">=</span> <span class="n">path_sep</span> <span class="ow">or</span> <span class="bp">self</span><span class="o">.</span><span class="n">path_sep</span>

        <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">uuid</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
            <span class="k">return</span> <span class="p">[]</span>

        <span class="n">values</span> <span class="o">=</span> <span class="p">[]</span>
        <span class="k">if</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">&quot;album&quot;</span><span class="p">:</span>
            <span class="n">values</span> <span class="o">=</span> <span class="p">(</span>
                <span class="nb">sorted</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">burst_albums</span><span class="p">)</span>
                <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">burst</span>
                <span class="k">else</span> <span class="nb">sorted</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">albums</span><span class="p">)</span>
            <span class="p">)</span>
        <span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">&quot;project&quot;</span><span class="p">:</span>
            <span class="n">values</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">([</span><span class="n">p</span><span class="o">.</span><span class="n">title</span> <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">project_info</span><span class="p">])</span>
        <span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">&quot;album_project&quot;</span><span class="p">:</span>
            <span class="n">values</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">burst_albums</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">burst</span> <span class="k">else</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">albums</span>
            <span class="n">values</span> <span class="o">+=</span> <span class="p">[</span><span class="n">p</span><span class="o">.</span><span class="n">title</span> <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">project_info</span><span class="p">]</span>
            <span class="n">values</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">values</span><span class="p">)</span>
        <span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">&quot;keyword&quot;</span><span class="p">:</span>
            <span class="n">values</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">keywords</span><span class="p">)</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">keywords</span> <span class="k">else</span> <span class="p">[]</span>
        <span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">&quot;person&quot;</span><span class="p">:</span>
            <span class="n">values</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">persons</span><span class="p">)</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">persons</span> <span class="k">else</span> <span class="p">[]</span>
            <span class="c1"># remove any _UNKNOWN_PERSON values</span>
            <span class="n">values</span> <span class="o">=</span> <span class="p">[</span><span class="n">val</span> <span class="k">for</span> <span class="n">val</span> <span class="ow">in</span> <span class="n">values</span> <span class="k">if</span> <span class="n">val</span> <span class="o">!=</span> <span class="n">_UNKNOWN_PERSON</span><span class="p">]</span>
        <span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">&quot;label&quot;</span><span class="p">:</span>
            <span class="n">values</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">labels</span><span class="p">)</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">labels</span> <span class="k">else</span> <span class="p">[]</span>
        <span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">&quot;label_normalized&quot;</span><span class="p">:</span>
            <span class="n">values</span> <span class="o">=</span> <span class="p">(</span>
                <span class="nb">sorted</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">labels_normalized</span><span class="p">)</span>
                <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">labels_normalized</span>
                <span class="k">else</span> <span class="p">[]</span>
            <span class="p">)</span>
        <span class="k">elif</span> <span class="n">field</span> <span class="ow">in</span> <span class="p">[</span><span class="s2">&quot;folder_album&quot;</span><span class="p">,</span> <span class="s2">&quot;folder_album_project&quot;</span><span class="p">]:</span>
            <span class="n">values</span> <span class="o">=</span> <span class="p">[]</span>
            <span class="c1"># photos must be in an album to be in a folder</span>
            <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">burst</span><span class="p">:</span>
                <span class="n">album_info</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">burst_album_info</span>
            <span class="k">else</span><span class="p">:</span>
                <span class="n">album_info</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">album_info</span>
            <span class="k">if</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">&quot;folder_album_project&quot;</span><span class="p">:</span>
                <span class="n">album_info</span> <span class="o">+=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">project_info</span>
            <span class="k">for</span> <span class="n">album</span> <span class="ow">in</span> <span class="n">album_info</span><span class="p">:</span>
                <span class="k">if</span> <span class="n">album</span><span class="o">.</span><span class="n">folder_names</span><span class="p">:</span>
                    <span class="c1"># album in folder</span>
                    <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">dirname</span><span class="p">:</span>
                        <span class="c1"># being used as a filepath so sanitize each part</span>
                        <span class="n">folder</span> <span class="o">=</span> <span class="n">path_sep</span><span class="o">.</span><span class="n">join</span><span class="p">(</span>
                            <span class="n">sanitize_dirname</span><span class="p">(</span><span class="n">f</span><span class="p">)</span> <span class="k">for</span> <span class="n">f</span> <span class="ow">in</span> <span class="n">album</span><span class="o">.</span><span class="n">folder_names</span>
                        <span class="p">)</span>
                        <span class="n">folder</span> <span class="o">+=</span> <span class="n">path_sep</span> <span class="o">+</span> <span class="n">sanitize_dirname</span><span class="p">(</span><span class="n">album</span><span class="o">.</span><span class="n">title</span><span class="p">)</span>
                    <span class="k">else</span><span class="p">:</span>
                        <span class="n">folder</span> <span class="o">=</span> <span class="n">path_sep</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">album</span><span class="o">.</span><span class="n">folder_names</span><span class="p">)</span>
                        <span class="n">folder</span> <span class="o">+=</span> <span class="n">path_sep</span> <span class="o">+</span> <span class="n">album</span><span class="o">.</span><span class="n">title</span>
                    <span class="n">values</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">folder</span><span class="p">)</span>
                <span class="k">elif</span> <span class="bp">self</span><span class="o">.</span><span class="n">dirname</span><span class="p">:</span>
                    <span class="n">values</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">sanitize_dirname</span><span class="p">(</span><span class="n">album</span><span class="o">.</span><span class="n">title</span><span class="p">))</span>
                <span class="k">else</span><span class="p">:</span>
                    <span class="n">values</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">album</span><span class="o">.</span><span class="n">title</span><span class="p">)</span>
            <span class="n">values</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">values</span><span class="p">)</span>
        <span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">&quot;comment&quot;</span><span class="p">:</span>
            <span class="n">values</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">(</span>
                <span class="p">[</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">comment</span><span class="o">.</span><span class="n">user</span><span class="si">}</span><span class="s2">: </span><span class="si">{</span><span class="n">comment</span><span class="o">.</span><span class="n">text</span><span class="si">}</span><span class="s2">&quot;</span> <span class="k">for</span> <span class="n">comment</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">comments</span><span class="p">]</span>
            <span class="p">)</span>
        <span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">&quot;searchinfo.holiday&quot;</span><span class="p">:</span>
            <span class="n">values</span> <span class="o">=</span> <span class="p">(</span>
                <span class="nb">sorted</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">search_info</span><span class="o">.</span><span class="n">holidays</span><span class="p">)</span>
                <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">search_info</span>
                <span class="k">else</span> <span class="p">[]</span>
            <span class="p">)</span>
        <span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">&quot;searchinfo.activity&quot;</span><span class="p">:</span>
            <span class="n">values</span> <span class="o">=</span> <span class="p">(</span>
                <span class="nb">sorted</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">search_info</span><span class="o">.</span><span class="n">activities</span><span class="p">)</span>
                <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">search_info</span>
                <span class="k">else</span> <span class="p">[]</span>
            <span class="p">)</span>
        <span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">&quot;searchinfo.venue&quot;</span><span class="p">:</span>
            <span class="n">values</span> <span class="o">=</span> <span class="p">(</span>
                <span class="nb">sorted</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">search_info</span><span class="o">.</span><span class="n">venues</span><span class="p">)</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">search_info</span> <span class="k">else</span> <span class="p">[]</span>
            <span class="p">)</span>
        <span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">&quot;searchinfo.venue_type&quot;</span><span class="p">:</span>
            <span class="n">values</span> <span class="o">=</span> <span class="p">(</span>
                <span class="nb">sorted</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">search_info</span><span class="o">.</span><span class="n">venue_types</span><span class="p">)</span>
                <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">search_info</span>
                <span class="k">else</span> <span class="p">[]</span>
            <span class="p">)</span>
        <span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">&quot;shell_quote&quot;</span><span class="p">:</span>
            <span class="n">values</span> <span class="o">=</span> <span class="p">[</span><span class="n">shlex</span><span class="o">.</span><span class="n">quote</span><span class="p">(</span><span class="n">v</span><span class="p">)</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">default</span> <span class="k">if</span> <span class="n">v</span><span class="p">]</span>
        <span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">&quot;strip&quot;</span><span class="p">:</span>
            <span class="n">values</span> <span class="o">=</span> <span class="p">[</span><span class="n">v</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">default</span><span class="p">]</span>
        <span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">&quot;format&quot;</span><span class="p">:</span>
            <span class="n">values</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_format_values</span><span class="p">(</span><span class="n">field</span><span class="p">,</span> <span class="n">subfield</span><span class="p">,</span> <span class="n">default</span><span class="p">)</span>
        <span class="k">elif</span> <span class="n">field</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s2">&quot;photo&quot;</span><span class="p">):</span>
            <span class="c1"># provide access to PhotoInfo object</span>
            <span class="n">properties</span> <span class="o">=</span> <span class="n">field</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot;.&quot;</span><span class="p">)</span>
            <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">properties</span><span class="p">)</span> <span class="o">&lt;=</span> <span class="mi">1</span><span class="p">:</span>
                <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span>
                    <span class="s2">&quot;Missing property in </span><span class="si">{photo}</span><span class="s2"> template.  Use in form </span><span class="si">{photo.property}</span><span class="s2">.&quot;</span>
                <span class="p">)</span>
            <span class="n">obj</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span>
            <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">properties</span><span class="p">)):</span>
                <span class="n">property_</span> <span class="o">=</span> <span class="n">properties</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
                <span class="k">try</span><span class="p">:</span>
                    <span class="n">obj</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="n">property_</span><span class="p">)</span>
                    <span class="k">if</span> <span class="n">obj</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
                        <span class="k">break</span>
                <span class="k">except</span> <span class="ne">AttributeError</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
                    <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span>
                        <span class="s2">&quot;Invalid property for </span><span class="si">{photo}</span><span class="s2"> template: &quot;</span> <span class="o">+</span> <span class="sa">f</span><span class="s2">&quot;&#39;</span><span class="si">{</span><span class="n">property_</span><span class="si">}</span><span class="s2">&#39;&quot;</span>
                    <span class="p">)</span> <span class="kn">from</span><span class="w"> </span><span class="nn">e</span>

            <span class="k">if</span> <span class="n">obj</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
                <span class="n">values</span> <span class="o">=</span> <span class="p">[]</span>
            <span class="k">elif</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="nb">bool</span><span class="p">):</span>
                <span class="n">values</span> <span class="o">=</span> <span class="p">[</span><span class="n">property_</span><span class="p">]</span> <span class="k">if</span> <span class="n">obj</span> <span class="k">else</span> <span class="p">[]</span>
            <span class="k">elif</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="p">(</span><span class="nb">str</span><span class="p">,</span> <span class="nb">int</span><span class="p">,</span> <span class="nb">float</span><span class="p">)):</span>
                <span class="n">values</span> <span class="o">=</span> <span class="p">[</span><span class="nb">str</span><span class="p">(</span><span class="n">obj</span><span class="p">)]</span>
            <span class="k">else</span><span class="p">:</span>
                <span class="k">try</span><span class="p">:</span>
                    <span class="n">values</span> <span class="o">=</span> <span class="p">[</span><span class="nb">str</span><span class="p">(</span><span class="n">o</span><span class="p">)</span> <span class="k">for</span> <span class="n">o</span> <span class="ow">in</span> <span class="n">obj</span><span class="p">]</span>
                <span class="k">except</span> <span class="ne">TypeError</span><span class="p">:</span>
                    <span class="n">values</span> <span class="o">=</span> <span class="p">[</span><span class="nb">str</span><span class="p">(</span><span class="n">obj</span><span class="p">)]</span>
        <span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">&quot;detected_text&quot;</span><span class="p">:</span>
            <span class="n">values</span> <span class="o">=</span> <span class="n">_get_detected_text</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="p">,</span> <span class="n">confidence</span><span class="o">=</span><span class="n">subfield</span><span class="p">)</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Unhandled template value: </span><span class="si">{</span><span class="n">field</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>

        <span class="c1"># sanitize directory names if needed, folder_album handled differently above</span>
        <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">filename</span><span class="p">:</span>
            <span class="n">values</span> <span class="o">=</span> <span class="p">[</span><span class="n">sanitize_pathpart</span><span class="p">(</span><span class="n">value</span><span class="p">)</span> <span class="k">for</span> <span class="n">value</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
        <span class="k">elif</span> <span class="bp">self</span><span class="o">.</span><span class="n">dirname</span> <span class="ow">and</span> <span class="n">field</span> <span class="ow">not</span> <span class="ow">in</span> <span class="p">[</span><span class="s2">&quot;folder_album&quot;</span><span class="p">,</span> <span class="s2">&quot;folder_album_project&quot;</span><span class="p">]:</span>
            <span class="c1"># skip folder_album because it would have been handled above</span>
            <span class="n">values</span> <span class="o">=</span> <span class="p">[</span><span class="n">sanitize_dirname</span><span class="p">(</span><span class="n">value</span><span class="p">)</span> <span class="k">for</span> <span class="n">value</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>

        <span class="k">return</span> <span class="n">values</span> <span class="ow">or</span> <span class="p">[]</span></div>


<div class="viewcode-block" id="PhotoTemplate.get_format_values">
<a class="viewcode-back" href="../../reference.html#osxphotos.PhotoTemplate.get_format_values">[docs]</a>
    <span class="k">def</span><span class="w"> </span><span class="nf">get_format_values</span><span class="p">(</span>
        <span class="bp">self</span><span class="p">,</span> <span class="n">field</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">subfield</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">default</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span>
    <span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Optional</span><span class="p">[</span><span class="n">List</span><span class="p">[</span><span class="n">Optional</span><span class="p">[</span><span class="nb">str</span><span class="p">]]]:</span>
<span class="w">        </span><span class="sd">&quot;&quot;&quot;Return values for {format} templates&quot;&quot;&quot;</span>

        <span class="k">if</span> <span class="n">field</span> <span class="o">!=</span> <span class="s2">&quot;format&quot;</span><span class="p">:</span>
            <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Unhandled template value in get_format_values: </span><span class="si">{</span><span class="n">field</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>

        <span class="k">if</span> <span class="ow">not</span> <span class="n">subfield</span> <span class="ow">or</span> <span class="s2">&quot;:&quot;</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">subfield</span><span class="p">:</span>
            <span class="k">raise</span> <span class="ne">SyntaxError</span><span class="p">(</span><span class="s2">&quot;</span><span class="si">{format}</span><span class="s2"> requires subfield in form TYPE:FORMAT&quot;</span><span class="p">)</span>
        <span class="n">type_</span><span class="p">,</span> <span class="n">format_str</span> <span class="o">=</span> <span class="n">subfield</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot;:&quot;</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">type_</span> <span class="ow">not</span> <span class="ow">in</span> <span class="p">(</span><span class="s2">&quot;int&quot;</span><span class="p">,</span> <span class="s2">&quot;float&quot;</span><span class="p">,</span> <span class="s2">&quot;str&quot;</span><span class="p">):</span>
            <span class="k">raise</span> <span class="ne">SyntaxError</span><span class="p">(</span>
                <span class="sa">f</span><span class="s2">&quot;&#39;</span><span class="si">{</span><span class="n">type_</span><span class="si">}</span><span class="s2">&#39; is not a valid type for </span><span class="si">{</span><span class="nb">format</span><span class="si">}</span><span class="s2">: must be one of &#39;int&#39;, &#39;float&#39;, &#39;str&#39;&quot;</span>
            <span class="p">)</span>
        <span class="k">if</span> <span class="n">type_</span> <span class="o">==</span> <span class="s2">&quot;int&quot;</span><span class="p">:</span>
            <span class="c1"># convert to float then int to avoid error when converting a string float to int</span>
            <span class="n">default_</span> <span class="o">=</span> <span class="p">[</span><span class="nb">int</span><span class="p">(</span><span class="nb">float</span><span class="p">(</span><span class="n">v</span><span class="p">))</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">default</span><span class="p">]</span>
        <span class="k">elif</span> <span class="n">type_</span> <span class="o">==</span> <span class="s2">&quot;float&quot;</span><span class="p">:</span>
            <span class="n">default_</span> <span class="o">=</span> <span class="p">[</span><span class="nb">float</span><span class="p">(</span><span class="n">v</span><span class="p">)</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">default</span><span class="p">]</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">default_</span> <span class="o">=</span> <span class="n">default</span>
        <span class="n">format_str</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">expand_variables_to_str</span><span class="p">(</span><span class="n">format_str</span><span class="p">,</span> <span class="s2">&quot;format string&quot;</span><span class="p">)</span>
        <span class="k">return</span> <span class="p">[</span><span class="n">format_str_value</span><span class="p">(</span><span class="n">v</span><span class="p">,</span> <span class="n">format_str</span><span class="p">)</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">default_</span><span class="p">]</span></div>


<div class="viewcode-block" id="PhotoTemplate.get_template_value_exiftool">
<a class="viewcode-back" href="../../reference.html#osxphotos.PhotoTemplate.get_template_value_exiftool">[docs]</a>
    <span class="k">def</span><span class="w"> </span><span class="nf">get_template_value_exiftool</span><span class="p">(</span>
        <span class="bp">self</span><span class="p">,</span>
        <span class="n">subfield</span><span class="p">,</span>
    <span class="p">):</span>
<span class="w">        </span><span class="sd">&quot;&quot;&quot;Get template value for format &quot;{exiftool:EXIF:Model}&quot; &quot;&quot;&quot;</span>

        <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
            <span class="k">return</span> <span class="p">[]</span>

        <span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">path</span><span class="p">:</span>
            <span class="k">return</span> <span class="p">[]</span>

        <span class="n">exif</span> <span class="o">=</span> <span class="n">ExifToolCaching</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">path</span><span class="p">,</span> <span class="n">exiftool</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">exiftool_path</span><span class="p">)</span>
        <span class="n">exifdict</span> <span class="o">=</span> <span class="n">exif</span><span class="o">.</span><span class="n">asdict</span><span class="p">(</span><span class="n">normalized</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
        <span class="n">subfield</span> <span class="o">=</span> <span class="n">subfield</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span>
        <span class="k">if</span> <span class="n">subfield</span> <span class="ow">in</span> <span class="n">exifdict</span><span class="p">:</span>
            <span class="n">values</span> <span class="o">=</span> <span class="n">exifdict</span><span class="p">[</span><span class="n">subfield</span><span class="p">]</span>
            <span class="n">values</span> <span class="o">=</span> <span class="n">values</span> <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">values</span><span class="p">,</span> <span class="nb">list</span><span class="p">)</span> <span class="k">else</span> <span class="p">[</span><span class="n">values</span><span class="p">]</span>
            <span class="n">values</span> <span class="o">=</span> <span class="p">[</span><span class="nb">str</span><span class="p">(</span><span class="n">v</span><span class="p">)</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>

            <span class="c1"># sanitize directory names if needed</span>
            <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">filename</span><span class="p">:</span>
                <span class="n">values</span> <span class="o">=</span> <span class="p">[</span><span class="n">sanitize_pathpart</span><span class="p">(</span><span class="n">value</span><span class="p">)</span> <span class="k">for</span> <span class="n">value</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
            <span class="k">elif</span> <span class="bp">self</span><span class="o">.</span><span class="n">dirname</span><span class="p">:</span>
                <span class="n">values</span> <span class="o">=</span> <span class="p">[</span><span class="n">sanitize_dirname</span><span class="p">(</span><span class="n">value</span><span class="p">)</span> <span class="k">for</span> <span class="n">value</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">values</span> <span class="o">=</span> <span class="p">[]</span>

        <span class="k">return</span> <span class="n">values</span></div>


<div class="viewcode-block" id="PhotoTemplate.get_template_value_function">
<a class="viewcode-back" href="../../reference.html#osxphotos.PhotoTemplate.get_template_value_function">[docs]</a>
    <span class="k">def</span><span class="w"> </span><span class="nf">get_template_value_function</span><span class="p">(</span>
        <span class="bp">self</span><span class="p">,</span>
        <span class="n">subfield</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
        <span class="n">field_arg</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">str</span><span class="p">],</span>
        <span class="n">caller</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
    <span class="p">):</span>
<span class="w">        </span><span class="sd">&quot;&quot;&quot;Get template value from external function</span>

<span class="sd">        Args:</span>
<span class="sd">            subfield: the filename and function name in for filename.py::function</span>
<span class="sd">            field_arg: the argument to pass to the function</span>
<span class="sd">            caller: the calling source of the template (&#39;export&#39; or &#39;import&#39;)</span>
<span class="sd">        &quot;&quot;&quot;</span>

        <span class="k">if</span> <span class="s2">&quot;::&quot;</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">subfield</span><span class="p">:</span>
            <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span>
                <span class="sa">f</span><span class="s2">&quot;SyntaxError: could not parse function name from &#39;</span><span class="si">{</span><span class="n">subfield</span><span class="si">}</span><span class="s2">&#39;&quot;</span>
            <span class="p">)</span>

        <span class="n">filename</span><span class="p">,</span> <span class="n">funcname</span> <span class="o">=</span> <span class="n">subfield</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot;::&quot;</span><span class="p">)</span>

        <span class="k">if</span> <span class="n">is_http_url</span><span class="p">(</span><span class="n">filename</span><span class="p">):</span>
            <span class="n">filename</span> <span class="o">=</span> <span class="n">download_url_to_temp_dir</span><span class="p">(</span><span class="n">filename</span><span class="p">)</span>
        <span class="n">filename_validated</span> <span class="o">=</span> <span class="n">expand_and_validate_filepath</span><span class="p">(</span><span class="n">filename</span><span class="p">)</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="n">filename_validated</span><span class="p">:</span>
            <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;&#39;</span><span class="si">{</span><span class="n">filename</span><span class="si">}</span><span class="s2">&#39; does not appear to be a file&quot;</span><span class="p">)</span>

        <span class="n">template_func</span> <span class="o">=</span> <span class="n">load_function</span><span class="p">(</span><span class="n">filename_validated</span><span class="p">,</span> <span class="n">funcname</span><span class="p">)</span>
        <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">uuid</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
            <span class="c1"># must be a PhotoInfoNone instance</span>
            <span class="c1"># if no uuid, then template is being validated but not actually run</span>
            <span class="c1"># so don&#39;t run the function</span>
            <span class="n">values</span> <span class="o">=</span> <span class="p">[]</span>
        <span class="k">elif</span> <span class="n">caller</span> <span class="ow">in</span> <span class="p">{</span><span class="s2">&quot;export&quot;</span><span class="p">,</span> <span class="s2">&quot;query&quot;</span><span class="p">}:</span>
            <span class="c1"># function signature is:</span>
            <span class="c1"># def example(photo: PhotoInfo, options: ExportOptions, args: Optional[str] = None, **kwargs) -&gt; Union[List, str]:</span>
            <span class="n">values</span> <span class="o">=</span> <span class="n">template_func</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="p">,</span> <span class="n">options</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="p">,</span> <span class="n">args</span><span class="o">=</span><span class="n">field_arg</span><span class="p">)</span>
        <span class="k">elif</span> <span class="n">caller</span> <span class="o">==</span> <span class="s2">&quot;import&quot;</span><span class="p">:</span>
            <span class="c1"># function signature is:</span>
            <span class="c1"># def example(filepath: pathlib.Path, args: Optional[str] = None, **kwargs) -&gt; Union[List, str]:</span>
            <span class="c1"># the PhotoInfoFromFile class used by import sets &#39;path&#39; to the path of the file being imported</span>
            <span class="n">values</span> <span class="o">=</span> <span class="n">template_func</span><span class="p">(</span><span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">path</span><span class="p">),</span> <span class="n">args</span><span class="o">=</span><span class="n">field_arg</span><span class="p">)</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Unhandled caller: </span><span class="si">{</span><span class="n">caller</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>

        <span class="k">if</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">values</span><span class="p">,</span> <span class="p">(</span><span class="nb">str</span><span class="p">,</span> <span class="nb">list</span><span class="p">)):</span>
            <span class="k">raise</span> <span class="ne">TypeError</span><span class="p">(</span>
                <span class="sa">f</span><span class="s2">&quot;Invalid return type for function </span><span class="si">{</span><span class="n">funcname</span><span class="si">}</span><span class="s2">: expected str or list&quot;</span>
            <span class="p">)</span>
        <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">values</span><span class="p">,</span> <span class="nb">str</span><span class="p">):</span>
            <span class="n">values</span> <span class="o">=</span> <span class="p">[</span><span class="n">values</span><span class="p">]</span>

        <span class="c1"># sanitize directory names if needed</span>
        <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">filename</span><span class="p">:</span>
            <span class="n">values</span> <span class="o">=</span> <span class="p">[</span><span class="n">sanitize_pathpart</span><span class="p">(</span><span class="n">value</span><span class="p">)</span> <span class="k">for</span> <span class="n">value</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
        <span class="k">elif</span> <span class="bp">self</span><span class="o">.</span><span class="n">dirname</span><span class="p">:</span>
            <span class="c1"># sanitize but don&#39;t replace any &quot;/&quot; as user function may want to create sub directories</span>
            <span class="n">values</span> <span class="o">=</span> <span class="p">[</span><span class="n">sanitize_dirname</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="n">replacement</span><span class="o">=</span><span class="kc">None</span><span class="p">)</span> <span class="k">for</span> <span class="n">value</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>

        <span class="k">return</span> <span class="n">values</span></div>


<div class="viewcode-block" id="PhotoTemplate.get_template_value_filter_function">
<a class="viewcode-back" href="../../reference.html#osxphotos.PhotoTemplate.get_template_value_filter_function">[docs]</a>
    <span class="k">def</span><span class="w"> </span><span class="nf">get_template_value_filter_function</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">filter_</span><span class="p">,</span> <span class="n">args</span><span class="p">,</span> <span class="n">values</span><span class="p">):</span>
<span class="w">        </span><span class="sd">&quot;&quot;&quot;Filter template value from external function&quot;&quot;&quot;</span>
        <span class="c1"># TODO: add args to filter function call? Would change signature of function</span>

        <span class="n">filter_</span> <span class="o">=</span> <span class="n">filter_</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s2">&quot;function:&quot;</span><span class="p">,</span> <span class="s2">&quot;&quot;</span><span class="p">)</span>

        <span class="k">if</span> <span class="s2">&quot;::&quot;</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">filter_</span><span class="p">:</span>
            <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span>
                <span class="sa">f</span><span class="s2">&quot;SyntaxError: could not parse function name from &#39;</span><span class="si">{</span><span class="n">filter_</span><span class="si">}</span><span class="s2">&#39;&quot;</span>
            <span class="p">)</span>

        <span class="n">filename</span><span class="p">,</span> <span class="n">funcname</span> <span class="o">=</span> <span class="n">filter_</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot;::&quot;</span><span class="p">)</span>

        <span class="n">filename_validated</span> <span class="o">=</span> <span class="n">expand_and_validate_filepath</span><span class="p">(</span><span class="n">filename</span><span class="p">)</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="n">filename_validated</span><span class="p">:</span>
            <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;&#39;</span><span class="si">{</span><span class="n">filename</span><span class="si">}</span><span class="s2">&#39; does not appear to be a file&quot;</span><span class="p">)</span>

        <span class="n">template_func</span> <span class="o">=</span> <span class="n">load_function</span><span class="p">(</span><span class="n">filename_validated</span><span class="p">,</span> <span class="n">funcname</span><span class="p">)</span>

        <span class="k">if</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">values</span><span class="p">,</span> <span class="p">(</span><span class="nb">list</span><span class="p">,</span> <span class="nb">tuple</span><span class="p">)):</span>
            <span class="n">values</span> <span class="o">=</span> <span class="p">[</span><span class="n">values</span><span class="p">]</span>

        <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">uuid</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
            <span class="c1"># if uuid is None, it&#39;s a PhotoInfoNone instance and template is being validated</span>
            <span class="c1"># so don&#39;t run the function</span>
            <span class="n">values</span> <span class="o">=</span> <span class="n">template_func</span><span class="p">(</span><span class="n">values</span><span class="p">)</span>

        <span class="k">if</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">values</span><span class="p">,</span> <span class="nb">list</span><span class="p">):</span>
            <span class="k">raise</span> <span class="ne">TypeError</span><span class="p">(</span>
                <span class="sa">f</span><span class="s2">&quot;Invalid return type for function </span><span class="si">{</span><span class="n">funcname</span><span class="si">}</span><span class="s2">: expected list&quot;</span>
            <span class="p">)</span>

        <span class="k">return</span> <span class="n">values</span></div>


<div class="viewcode-block" id="PhotoTemplate.get_photo_video_type">
<a class="viewcode-back" href="../../reference.html#osxphotos.PhotoTemplate.get_photo_video_type">[docs]</a>
    <span class="k">def</span><span class="w"> </span><span class="nf">get_photo_video_type</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">default</span><span class="p">):</span>
<span class="w">        </span><span class="sd">&quot;&quot;&quot;return media type, e.g. photo or video&quot;&quot;&quot;</span>
        <span class="n">default_dict</span> <span class="o">=</span> <span class="n">parse_default_kv</span><span class="p">(</span><span class="n">default</span><span class="p">,</span> <span class="n">PHOTO_VIDEO_TYPE_DEFAULTS</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">default_dict</span><span class="p">[</span><span class="s2">&quot;photo&quot;</span><span class="p">]</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">isphoto</span> <span class="k">else</span> <span class="n">default_dict</span><span class="p">[</span><span class="s2">&quot;video&quot;</span><span class="p">]</span></div>


<div class="viewcode-block" id="PhotoTemplate.get_media_type">
<a class="viewcode-back" href="../../reference.html#osxphotos.PhotoTemplate.get_media_type">[docs]</a>
    <span class="k">def</span><span class="w"> </span><span class="nf">get_media_type</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">default</span><span class="p">):</span>
<span class="w">        </span><span class="sd">&quot;&quot;&quot;return special media type, e.g. slow_mo, panorama, etc., defaults to photo or video if no special type&quot;&quot;&quot;</span>
        <span class="n">default_dict</span> <span class="o">=</span> <span class="n">parse_default_kv</span><span class="p">(</span><span class="n">default</span><span class="p">,</span> <span class="n">MEDIA_TYPE_DEFAULTS</span><span class="p">)</span>
        <span class="n">p</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span>
        <span class="k">if</span> <span class="n">p</span><span class="o">.</span><span class="n">selfie</span><span class="p">:</span>
            <span class="k">return</span> <span class="n">default_dict</span><span class="p">[</span><span class="s2">&quot;selfie&quot;</span><span class="p">]</span>
        <span class="k">elif</span> <span class="n">p</span><span class="o">.</span><span class="n">time_lapse</span><span class="p">:</span>
            <span class="k">return</span> <span class="n">default_dict</span><span class="p">[</span><span class="s2">&quot;time_lapse&quot;</span><span class="p">]</span>
        <span class="k">elif</span> <span class="n">p</span><span class="o">.</span><span class="n">panorama</span><span class="p">:</span>
            <span class="k">return</span> <span class="n">default_dict</span><span class="p">[</span><span class="s2">&quot;panorama&quot;</span><span class="p">]</span>
        <span class="k">elif</span> <span class="n">p</span><span class="o">.</span><span class="n">slow_mo</span><span class="p">:</span>
            <span class="k">return</span> <span class="n">default_dict</span><span class="p">[</span><span class="s2">&quot;slow_mo&quot;</span><span class="p">]</span>
        <span class="k">elif</span> <span class="n">p</span><span class="o">.</span><span class="n">screenshot</span><span class="p">:</span>
            <span class="k">return</span> <span class="n">default_dict</span><span class="p">[</span><span class="s2">&quot;screenshot&quot;</span><span class="p">]</span>
        <span class="k">elif</span> <span class="n">p</span><span class="o">.</span><span class="n">screen_recording</span><span class="p">:</span>
            <span class="k">return</span> <span class="n">default_dict</span><span class="p">[</span><span class="s2">&quot;screen_recording&quot;</span><span class="p">]</span>
        <span class="k">elif</span> <span class="n">p</span><span class="o">.</span><span class="n">portrait</span><span class="p">:</span>
            <span class="k">return</span> <span class="n">default_dict</span><span class="p">[</span><span class="s2">&quot;portrait&quot;</span><span class="p">]</span>
        <span class="k">elif</span> <span class="n">p</span><span class="o">.</span><span class="n">live_photo</span><span class="p">:</span>
            <span class="k">return</span> <span class="n">default_dict</span><span class="p">[</span><span class="s2">&quot;live_photo&quot;</span><span class="p">]</span>
        <span class="k">elif</span> <span class="n">p</span><span class="o">.</span><span class="n">burst</span><span class="p">:</span>
            <span class="k">return</span> <span class="n">default_dict</span><span class="p">[</span><span class="s2">&quot;burst&quot;</span><span class="p">]</span>
        <span class="k">elif</span> <span class="n">p</span><span class="o">.</span><span class="n">ismovie</span><span class="p">:</span>
            <span class="k">return</span> <span class="n">default_dict</span><span class="p">[</span><span class="s2">&quot;video&quot;</span><span class="p">]</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="k">return</span> <span class="n">default_dict</span><span class="p">[</span><span class="s2">&quot;photo&quot;</span><span class="p">]</span></div>


<div class="viewcode-block" id="PhotoTemplate.get_photo_bool_attribute">
<a class="viewcode-back" href="../../reference.html#osxphotos.PhotoTemplate.get_photo_bool_attribute">[docs]</a>
    <span class="k">def</span><span class="w"> </span><span class="nf">get_photo_bool_attribute</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">attr</span><span class="p">,</span> <span class="n">default</span><span class="p">,</span> <span class="n">bool_val</span><span class="p">):</span>
<span class="w">        </span><span class="sd">&quot;&quot;&quot;Return the boolean value for a photo attribute&quot;&quot;&quot;</span>
        <span class="k">return</span> <span class="n">bool_val</span> <span class="k">if</span> <span class="p">(</span><span class="n">val</span> <span class="o">:=</span> <span class="nb">getattr</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="p">,</span> <span class="n">attr</span><span class="p">))</span> <span class="k">else</span> <span class="n">default</span></div>
</div>



<span class="k">def</span><span class="w"> </span><span class="nf">parse_default_kv</span><span class="p">(</span><span class="n">default</span><span class="p">,</span> <span class="n">default_dict</span><span class="p">):</span>
<span class="w">    </span><span class="sd">&quot;&quot;&quot;parse a string in form key1=value1;key2=value2,... as used for some template fields</span>

<span class="sd">    Args:</span>
<span class="sd">        default: str, in form &#39;photo=foto;video=vidéo&#39;</span>
<span class="sd">        default_dict: dict, in form {&quot;photo&quot;: &quot;fotos&quot;, &quot;video&quot;: &quot;vidéos&quot;} with default values</span>

<span class="sd">    Returns:</span>
<span class="sd">        dict in form {&quot;photo&quot;: &quot;fotos&quot;, &quot;video&quot;: &quot;vidéos&quot;}</span>
<span class="sd">    &quot;&quot;&quot;</span>

    <span class="n">default_dict_</span> <span class="o">=</span> <span class="n">default_dict</span><span class="o">.</span><span class="n">copy</span><span class="p">()</span>
    <span class="k">if</span> <span class="n">default</span><span class="p">:</span>
        <span class="n">defaults</span> <span class="o">=</span> <span class="n">default</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot;;&quot;</span><span class="p">)</span>
        <span class="k">for</span> <span class="n">kv</span> <span class="ow">in</span> <span class="n">defaults</span><span class="p">:</span>
            <span class="k">try</span><span class="p">:</span>
                <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="o">=</span> <span class="n">kv</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot;=&quot;</span><span class="p">)</span>
                <span class="n">k</span> <span class="o">=</span> <span class="n">k</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
                <span class="n">v</span> <span class="o">=</span> <span class="n">v</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
                <span class="n">default_dict_</span><span class="p">[</span><span class="n">k</span><span class="p">]</span> <span class="o">=</span> <span class="n">v</span>
            <span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
                <span class="k">pass</span>
    <span class="k">return</span> <span class="n">default_dict_</span>


<span class="k">def</span><span class="w"> </span><span class="nf">get_template_help</span><span class="p">():</span>
<span class="w">    </span><span class="sd">&quot;&quot;&quot;Return help for template system as markdown string&quot;&quot;&quot;</span>
    <span class="c1"># TODO: would be better to use importlib.abc.ResourceReader but I can&#39;t find a single example of how to do this</span>
    <span class="n">help_file</span> <span class="o">=</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="vm">__file__</span><span class="p">)</span><span class="o">.</span><span class="n">parent</span> <span class="o">/</span> <span class="s2">&quot;phototemplate.md&quot;</span>
    <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">help_file</span><span class="p">,</span> <span class="s2">&quot;r&quot;</span><span class="p">)</span> <span class="k">as</span> <span class="n">fd</span><span class="p">:</span>
        <span class="n">md</span> <span class="o">=</span> <span class="n">fd</span><span class="o">.</span><span class="n">read</span><span class="p">()</span>
    <span class="k">return</span> <span class="n">md</span>


<span class="k">def</span><span class="w"> </span><span class="nf">get_template_field_table</span><span class="p">():</span>
<span class="w">    </span><span class="sd">&quot;&quot;&quot;Return markdown table of template field substitutions&quot;&quot;&quot;</span>
    <span class="n">template_table</span> <span class="o">=</span> <span class="s2">&quot;| Field | Description |&quot;</span> <span class="o">+</span> <span class="s2">&quot;</span><span class="se">\n</span><span class="s2">|--------------|-------------|&quot;</span>
    <span class="k">for</span> <span class="n">subst</span><span class="p">,</span> <span class="n">descr</span> <span class="ow">in</span> <span class="p">[</span>
        <span class="o">*</span><span class="n">TEMPLATE_SUBSTITUTIONS</span><span class="o">.</span><span class="n">items</span><span class="p">(),</span>
        <span class="o">*</span><span class="n">TEMPLATE_SUBSTITUTIONS_MULTI_VALUED</span><span class="o">.</span><span class="n">items</span><span class="p">(),</span>
    <span class="p">]:</span>
        <span class="c1"># replace &#39;|&#39; with &#39;\|&#39; to avoid markdown parsing issues (e.g. in {pipe} description)</span>
        <span class="n">descr</span> <span class="o">=</span> <span class="n">descr</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s2">&quot;&#39;|&#39;&quot;</span><span class="p">,</span> <span class="sa">r</span><span class="s2">&quot;&#39;\|&#39;&quot;</span><span class="p">)</span>
        <span class="n">template_table</span> <span class="o">+=</span> <span class="sa">f</span><span class="s2">&quot;</span><span class="se">\n</span><span class="s2">|</span><span class="si">{</span><span class="n">subst</span><span class="si">}</span><span class="s2">|</span><span class="si">{</span><span class="n">descr</span><span class="si">}</span><span class="s2">|&quot;</span>
    <span class="k">return</span> <span class="n">template_table</span>


<span class="k">def</span><span class="w"> </span><span class="nf">_get_pathlib_value</span><span class="p">(</span><span class="n">field</span><span class="p">,</span> <span class="n">value</span><span class="p">,</span> <span class="n">quote</span><span class="p">):</span>
<span class="w">    </span><span class="sd">&quot;&quot;&quot;Get the value for a pathlib.Path type template</span>

<span class="sd">    Args:</span>
<span class="sd">        field: the path field, e.g. &quot;filepath.stem&quot;</span>
<span class="sd">        value: the value for the path component</span>
<span class="sd">        quote: bool; if true, quotes the returned path for safe execution in the shell</span>
<span class="sd">    &quot;&quot;&quot;</span>
    <span class="n">parts</span> <span class="o">=</span> <span class="n">field</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot;.&quot;</span><span class="p">)</span>
    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">parts</span><span class="p">)</span> <span class="o">==</span> <span class="mi">1</span><span class="p">:</span>
        <span class="k">return</span> <span class="n">shlex</span><span class="o">.</span><span class="n">quote</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">value</span><span class="p">))</span> <span class="k">if</span> <span class="n">quote</span> <span class="k">else</span> <span class="nb">str</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>

    <span class="n">path</span> <span class="o">=</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
    <span class="k">for</span> <span class="n">attribute</span> <span class="ow">in</span> <span class="n">parts</span><span class="p">[</span><span class="mi">1</span><span class="p">:]:</span>
        <span class="k">try</span><span class="p">:</span>
            <span class="n">val</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">attribute</span><span class="p">)</span>
            <span class="n">path</span> <span class="o">=</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="n">val</span><span class="p">)</span>
        <span class="k">except</span> <span class="ne">AttributeError</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
            <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Illegal value for filepath template: </span><span class="si">{</span><span class="n">attribute</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span> <span class="kn">from</span><span class="w"> </span><span class="nn">e</span>

    <span class="n">val_str</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">val</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">quote</span><span class="p">:</span>
        <span class="n">val_str</span> <span class="o">=</span> <span class="n">shlex</span><span class="o">.</span><span class="n">quote</span><span class="p">(</span><span class="n">val_str</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">val_str</span>


<span class="k">def</span><span class="w"> </span><span class="nf">format_str_value</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="n">format_str</span><span class="p">):</span>
<span class="w">    </span><span class="sd">&quot;&quot;&quot;Format value based on format code in field in format id:02d&quot;&quot;&quot;</span>
    <span class="k">if</span> <span class="ow">not</span> <span class="n">format_str</span><span class="p">:</span>
        <span class="k">return</span> <span class="nb">str</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
    <span class="n">format_str</span> <span class="o">=</span> <span class="s2">&quot;{0:&quot;</span> <span class="o">+</span> <span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">format_str</span><span class="si">}</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="s2">&quot;}&quot;</span>
    <span class="k">return</span> <span class="n">format_str</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>


<span class="k">def</span><span class="w"> </span><span class="nf">_get_album_by_name</span><span class="p">(</span><span class="n">photo</span><span class="p">,</span> <span class="n">album</span><span class="p">):</span>
<span class="w">    </span><span class="sd">&quot;&quot;&quot;Finds first album named album that photo is in and returns the AlbumInfo object, otherwise returns None&quot;&quot;&quot;</span>
    <span class="k">return</span> <span class="nb">next</span><span class="p">(</span>
        <span class="p">(</span><span class="n">album_info</span> <span class="k">for</span> <span class="n">album_info</span> <span class="ow">in</span> <span class="n">photo</span><span class="o">.</span><span class="n">album_info</span> <span class="k">if</span> <span class="n">album_info</span><span class="o">.</span><span class="n">title</span> <span class="o">==</span> <span class="n">album</span><span class="p">),</span>
        <span class="kc">None</span><span class="p">,</span>
    <span class="p">)</span>


<span class="k">def</span><span class="w"> </span><span class="nf">_get_album_by_path</span><span class="p">(</span><span class="n">photo</span><span class="p">,</span> <span class="n">folder_album_path</span><span class="p">):</span>
<span class="w">    </span><span class="sd">&quot;&quot;&quot;finds the first album whose folder_album path matches and folder_album_path and returns the AlbumInfo object, otherwise, returns None&quot;&quot;&quot;</span>
    <span class="k">for</span> <span class="n">album_info</span> <span class="ow">in</span> <span class="n">photo</span><span class="o">.</span><span class="n">album_info</span><span class="p">:</span>
        <span class="c1"># following code is how {folder_album} builds the folder path</span>
        <span class="n">folder</span> <span class="o">=</span> <span class="s2">&quot;/&quot;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">sanitize_dirname</span><span class="p">(</span><span class="n">f</span><span class="p">)</span> <span class="k">for</span> <span class="n">f</span> <span class="ow">in</span> <span class="n">album_info</span><span class="o">.</span><span class="n">folder_names</span><span class="p">)</span>
        <span class="n">folder</span> <span class="o">+=</span> <span class="sa">f</span><span class="s2">&quot;/</span><span class="si">{</span><span class="n">sanitize_dirname</span><span class="p">(</span><span class="n">album_info</span><span class="o">.</span><span class="n">title</span><span class="p">)</span><span class="si">}</span><span class="s2">&quot;</span>
        <span class="k">if</span> <span class="n">folder_album_path</span><span class="o">.</span><span class="n">endswith</span><span class="p">(</span><span class="n">folder</span><span class="p">):</span>
            <span class="k">return</span> <span class="n">album_info</span>
    <span class="k">return</span> <span class="kc">None</span>


<span class="k">def</span><span class="w"> </span><span class="nf">_get_detected_text</span><span class="p">(</span><span class="n">photo</span><span class="p">,</span> <span class="n">confidence</span><span class="o">=</span><span class="n">TEXT_DETECTION_CONFIDENCE_THRESHOLD</span><span class="p">):</span>
<span class="w">    </span><span class="sd">&quot;&quot;&quot;Returns the detected text for a photo</span>
<span class="sd">    {detected_text} uses this instead of PhotoInfo.detected_text() to cache the text for all confidence values</span>
<span class="sd">    &quot;&quot;&quot;</span>
    <span class="k">if</span> <span class="ow">not</span> <span class="n">photo</span><span class="o">.</span><span class="n">isphoto</span><span class="p">:</span>
        <span class="k">return</span> <span class="p">[]</span>

    <span class="n">confidence</span> <span class="o">=</span> <span class="p">(</span>
        <span class="nb">float</span><span class="p">(</span><span class="n">confidence</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">confidence</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span>
        <span class="k">else</span> <span class="n">TEXT_DETECTION_CONFIDENCE_THRESHOLD</span>
    <span class="p">)</span>

    <span class="c1"># _detected_text caches the text detection results in an extended attribute</span>
    <span class="c1"># so the first time this gets called is slow but repeated accesses are fast</span>
    <span class="n">detected_text</span> <span class="o">=</span> <span class="n">photo</span><span class="o">.</span><span class="n">_detected_text</span><span class="p">()</span>
    <span class="k">return</span> <span class="p">[</span><span class="n">text</span> <span class="k">for</span> <span class="n">text</span><span class="p">,</span> <span class="n">conf</span> <span class="ow">in</span> <span class="n">detected_text</span> <span class="k">if</span> <span class="n">conf</span> <span class="o">&gt;=</span> <span class="n">confidence</span><span class="p">]</span>


<span class="k">def</span><span class="w"> </span><span class="nf">create_slice</span><span class="p">(</span><span class="n">args</span><span class="p">):</span>
<span class="w">    </span><span class="sd">&quot;&quot;&quot;Create a slice object from a string of args in form &quot;start:end:step&quot; &quot;&quot;&quot;</span>
    <span class="n">slice_args</span> <span class="o">=</span> <span class="n">args</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot;:&quot;</span><span class="p">)</span>
    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">slice_args</span><span class="p">)</span> <span class="o">==</span> <span class="mi">1</span><span class="p">:</span>
        <span class="n">start</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">slice_args</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="ow">or</span> <span class="mi">0</span><span class="p">)</span>
        <span class="n">end</span> <span class="o">=</span> <span class="kc">None</span>
        <span class="n">step</span> <span class="o">=</span> <span class="kc">None</span>
    <span class="k">elif</span> <span class="nb">len</span><span class="p">(</span><span class="n">slice_args</span><span class="p">)</span> <span class="o">==</span> <span class="mi">2</span><span class="p">:</span>
        <span class="n">start</span><span class="p">,</span> <span class="n">end</span> <span class="o">=</span> <span class="n">slice_args</span>
        <span class="n">start</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">start</span><span class="p">)</span> <span class="k">if</span> <span class="n">start</span> <span class="o">!=</span> <span class="s2">&quot;&quot;</span> <span class="k">else</span> <span class="kc">None</span>
        <span class="n">end</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">end</span><span class="p">)</span> <span class="k">if</span> <span class="n">end</span> <span class="o">!=</span> <span class="s2">&quot;&quot;</span> <span class="k">else</span> <span class="kc">None</span>
        <span class="n">step</span> <span class="o">=</span> <span class="kc">None</span>
    <span class="k">elif</span> <span class="nb">len</span><span class="p">(</span><span class="n">slice_args</span><span class="p">)</span> <span class="o">==</span> <span class="mi">3</span><span class="p">:</span>
        <span class="n">start</span><span class="p">,</span> <span class="n">end</span><span class="p">,</span> <span class="n">step</span> <span class="o">=</span> <span class="n">slice_args</span>
        <span class="n">start</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">start</span><span class="p">)</span> <span class="k">if</span> <span class="n">start</span> <span class="o">!=</span> <span class="s2">&quot;&quot;</span> <span class="k">else</span> <span class="kc">None</span>
        <span class="n">end</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">end</span><span class="p">)</span> <span class="k">if</span> <span class="n">end</span> <span class="o">!=</span> <span class="s2">&quot;&quot;</span> <span class="k">else</span> <span class="kc">None</span>
        <span class="n">step</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">step</span><span class="p">)</span> <span class="k">if</span> <span class="n">step</span> <span class="o">!=</span> <span class="s2">&quot;&quot;</span> <span class="k">else</span> <span class="kc">None</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="k">raise</span> <span class="ne">SyntaxError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Invalid slice: </span><span class="si">{</span><span class="n">args</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
    <span class="k">return</span> <span class="nb">slice</span><span class="p">(</span><span class="n">start</span><span class="p">,</span> <span class="n">end</span><span class="p">,</span> <span class="n">step</span><span class="p">)</span>


<span class="k">def</span><span class="w"> </span><span class="nf">values_to_int</span><span class="p">(</span><span class="n">values</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">]:</span>
<span class="w">    </span><span class="sd">&quot;&quot;&quot;Convert a list of strings to str representation of ints, if possible, otherwise strip values from list&quot;&quot;&quot;</span>
    <span class="n">int_values</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">:</span>
        <span class="k">with</span> <span class="n">suppress</span><span class="p">(</span><span class="ne">ValueError</span><span class="p">):</span>
            <span class="n">int_values</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="nb">float</span><span class="p">(</span><span class="n">v</span><span class="p">))))</span>
    <span class="k">return</span> <span class="n">int_values</span>


<span class="k">def</span><span class="w"> </span><span class="nf">values_to_float</span><span class="p">(</span><span class="n">values</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">]:</span>
<span class="w">    </span><span class="sd">&quot;&quot;&quot;Convert a list of strings to str representation of float, if possible, otherwise strip values from list&quot;&quot;&quot;</span>
    <span class="n">float_values</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">:</span>
        <span class="k">with</span> <span class="n">suppress</span><span class="p">(</span><span class="ne">ValueError</span><span class="p">):</span>
            <span class="n">float_values</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="nb">float</span><span class="p">(</span><span class="n">v</span><span class="p">)))</span>
    <span class="k">return</span> <span class="n">float_values</span>


<span class="k">def</span><span class="w"> </span><span class="nf">format_date_field</span><span class="p">(</span><span class="n">dt</span><span class="p">:</span> <span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="p">,</span> <span class="n">field</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">args</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
<span class="w">    </span><span class="sd">&quot;&quot;&quot;Format a date template field in format &#39;created&#39;, &#39;create.year&#39; etc.</span>

<span class="sd">    Args:</span>
<span class="sd">        dt: datetime object</span>
<span class="sd">        field: the field to format, e.g. &#39;created.year&#39;, &#39;today.strftime&#39;</span>
<span class="sd">        args: the argument to the field, e.g. &#39;%Y&#39; for strftime</span>
<span class="sd">    &quot;&quot;&quot;</span>
    <span class="n">fields</span> <span class="o">=</span> <span class="n">field</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot;.&quot;</span><span class="p">)</span>
    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">fields</span><span class="p">)</span> <span class="o">==</span> <span class="mi">1</span><span class="p">:</span>
        <span class="c1"># no subfield, just return the formatted date str</span>
        <span class="k">return</span> <span class="n">dt</span><span class="o">.</span><span class="n">date</span><span class="p">()</span><span class="o">.</span><span class="n">isoformat</span><span class="p">()</span>
    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">fields</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">2</span><span class="p">:</span>
        <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Unhandled template value: </span><span class="si">{</span><span class="n">field</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
    <span class="n">subfield</span> <span class="o">=</span> <span class="n">fields</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
    <span class="k">if</span> <span class="n">subfield</span> <span class="o">==</span> <span class="s2">&quot;strftime&quot;</span><span class="p">:</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="n">args</span><span class="p">:</span>
            <span class="k">return</span> <span class="kc">None</span>
        <span class="k">try</span><span class="p">:</span>
            <span class="k">return</span> <span class="n">dt</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="n">args</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
        <span class="k">except</span><span class="p">:</span>
            <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Invalid strftime template: &#39;</span><span class="si">{</span><span class="n">args</span><span class="si">}</span><span class="s2">&#39;&quot;</span><span class="p">)</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="k">try</span><span class="p">:</span>
            <span class="k">return</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">DateTimeFormatter</span><span class="p">(</span><span class="n">dt</span><span class="p">),</span> <span class="n">subfield</span><span class="p">)</span>
        <span class="k">except</span> <span class="ne">AttributeError</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
            <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Unhandled template value: </span><span class="si">{</span><span class="n">field</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span> <span class="kn">from</span><span class="w"> </span><span class="nn">e</span>


<span class="k">def</span><span class="w"> </span><span class="nf">get_place_value</span><span class="p">(</span><span class="n">photo</span><span class="p">:</span> <span class="s2">&quot;PhotoInfo&quot;</span><span class="p">,</span> <span class="n">field</span><span class="p">:</span> <span class="nb">str</span><span class="p">):</span>  <span class="c1"># noqa: F821</span>
<span class="w">    </span><span class="sd">&quot;&quot;&quot;Get the value of a &#39;place&#39; field by attribute</span>

<span class="sd">    Args:</span>
<span class="sd">        photo: the PhotoInfo object</span>
<span class="sd">        field: the field to get, e.g. &#39;place.name&#39;</span>
<span class="sd">    &quot;&quot;&quot;</span>
    <span class="k">if</span> <span class="ow">not</span> <span class="n">photo</span><span class="o">.</span><span class="n">place</span><span class="p">:</span>
        <span class="k">return</span> <span class="kc">None</span>

    <span class="n">fields</span> <span class="o">=</span> <span class="n">field</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot;.&quot;</span><span class="p">)</span>
    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">fields</span><span class="p">)</span> <span class="o">&lt;</span> <span class="mi">2</span><span class="p">:</span>
        <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Invalid place field: </span><span class="si">{</span><span class="n">field</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
    <span class="n">subfields</span> <span class="o">=</span> <span class="n">fields</span><span class="p">[</span><span class="mi">1</span><span class="p">:]</span>
    <span class="k">if</span> <span class="n">subfields</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="ow">in</span> <span class="p">[</span><span class="s2">&quot;name&quot;</span><span class="p">,</span> <span class="s2">&quot;country_code&quot;</span><span class="p">]</span> <span class="ow">and</span> <span class="nb">len</span><span class="p">(</span><span class="n">subfields</span><span class="p">)</span> <span class="o">==</span> <span class="mi">1</span><span class="p">:</span>
        <span class="k">return</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">photo</span><span class="o">.</span><span class="n">place</span><span class="p">,</span> <span class="n">subfields</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> <span class="ow">or</span> <span class="kc">None</span>
    <span class="k">elif</span> <span class="n">subfields</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="s2">&quot;name&quot;</span> <span class="ow">and</span> <span class="nb">len</span><span class="p">(</span><span class="n">subfields</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">1</span><span class="p">:</span>
        <span class="k">if</span> <span class="n">subfields</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="s2">&quot;country&quot;</span><span class="p">:</span>
            <span class="k">return</span> <span class="n">photo</span><span class="o">.</span><span class="n">place</span><span class="o">.</span><span class="n">names</span><span class="o">.</span><span class="n">country</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="k">if</span> <span class="n">photo</span><span class="o">.</span><span class="n">place</span><span class="o">.</span><span class="n">names</span><span class="o">.</span><span class="n">country</span> <span class="k">else</span> <span class="kc">None</span>
        <span class="k">elif</span> <span class="n">subfields</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="s2">&quot;state_province&quot;</span><span class="p">:</span>
            <span class="k">return</span> <span class="p">(</span>
                <span class="n">photo</span><span class="o">.</span><span class="n">place</span><span class="o">.</span><span class="n">names</span><span class="o">.</span><span class="n">state_province</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
                <span class="k">if</span> <span class="n">photo</span><span class="o">.</span><span class="n">place</span><span class="o">.</span><span class="n">names</span><span class="o">.</span><span class="n">state_province</span>
                <span class="k">else</span> <span class="kc">None</span>
            <span class="p">)</span>
        <span class="k">elif</span> <span class="n">subfields</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="s2">&quot;city&quot;</span><span class="p">:</span>
            <span class="k">return</span> <span class="n">photo</span><span class="o">.</span><span class="n">place</span><span class="o">.</span><span class="n">names</span><span class="o">.</span><span class="n">city</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="k">if</span> <span class="n">photo</span><span class="o">.</span><span class="n">place</span><span class="o">.</span><span class="n">names</span><span class="o">.</span><span class="n">city</span> <span class="k">else</span> <span class="kc">None</span>
        <span class="k">elif</span> <span class="n">subfields</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="s2">&quot;area_of_interest&quot;</span><span class="p">:</span>
            <span class="k">return</span> <span class="p">(</span>
                <span class="n">photo</span><span class="o">.</span><span class="n">place</span><span class="o">.</span><span class="n">names</span><span class="o">.</span><span class="n">area_of_interest</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
                <span class="k">if</span> <span class="n">photo</span><span class="o">.</span><span class="n">place</span><span class="o">.</span><span class="n">names</span><span class="o">.</span><span class="n">area_of_interest</span>
                <span class="k">else</span> <span class="kc">None</span>
            <span class="p">)</span>
    <span class="k">elif</span> <span class="n">subfields</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="s2">&quot;address&quot;</span><span class="p">:</span>
        <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">subfields</span><span class="p">)</span> <span class="o">==</span> <span class="mi">1</span><span class="p">:</span>
            <span class="k">return</span> <span class="n">photo</span><span class="o">.</span><span class="n">place</span><span class="o">.</span><span class="n">address_str</span>
        <span class="k">elif</span> <span class="n">subfields</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="ow">in</span> <span class="p">[</span>
            <span class="s2">&quot;street&quot;</span><span class="p">,</span>
            <span class="s2">&quot;city&quot;</span><span class="p">,</span>
            <span class="s2">&quot;state_province&quot;</span><span class="p">,</span>
            <span class="s2">&quot;postal_code&quot;</span><span class="p">,</span>
            <span class="s2">&quot;country&quot;</span><span class="p">,</span>
        <span class="p">]:</span>
            <span class="k">return</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">photo</span><span class="o">.</span><span class="n">place</span><span class="o">.</span><span class="n">address</span><span class="p">,</span> <span class="n">subfields</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span> <span class="ow">or</span> <span class="kc">None</span>
        <span class="k">elif</span> <span class="n">subfields</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="s2">&quot;country_code&quot;</span><span class="p">:</span>
            <span class="k">return</span> <span class="n">photo</span><span class="o">.</span><span class="n">place</span><span class="o">.</span><span class="n">address</span><span class="o">.</span><span class="n">iso_country_code</span> <span class="ow">or</span> <span class="kc">None</span>
    <span class="c1"># did not find a match</span>
    <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Unhandled template value: </span><span class="si">{</span><span class="n">field</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
</pre></div>
        </article>
      </div>
      <footer>
        
        <div class="related-pages">
          
          
        </div>
        <div class="bottom-of-page">
          <div class="left-details">
            <div class="copyright">
                Copyright &#169; 2021, Rhet Turnbull
            </div>
            Made with <a href="https://www.sphinx-doc.org/">Sphinx</a> and <a class="muted-link" href="https://pradyunsg.me">@pradyunsg</a>'s
            
            <a href="https://github.com/pradyunsg/furo">Furo</a>
            
          </div>
          <div class="right-details">
            
          </div>
        </div>
        
      </footer>
    </div>
    <aside class="toc-drawer no-toc">
      
      
      
    </aside>
  </div>
</div><script src="../../_static/documentation_options.js?v=63568c49"></script>
    <script src="../../_static/doctools.js?v=9bcbadda"></script>
    <script src="../../_static/sphinx_highlight.js?v=dc90522c"></script>
    <script src="../../_static/scripts/furo.js?v=46bd48cc"></script>
    <script src="../../_static/clipboard.min.js?v=a7894cd8"></script>
    <script src="../../_static/copybutton.js?v=f281be69"></script>
    </body>
</html>